1. 程式人生 > >火柴排隊(NOIP2013)(附樹狀數組專題講解(其實只是粗略。。。))

火柴排隊(NOIP2013)(附樹狀數組專題講解(其實只是粗略。。。))

blog 規律 混亂 ace urn ets gets value update

原題傳送門。。(9018上不去。明天再來搞。)

首先,這道題目是一道神奇的題。

看到這道題,第一眼就覺得2個數組排個序,然後一一對應的時候一定差值最小。

由於我們可以將這2個數列同時進行調換。

所以我們先把2個數列排個序。

第二個序列中的數組的下標都指向第一個數組中的數的原來位置(其實就是離散化(真是啰嗦。。))

離散化之後,我們就變成了一個混亂的數列變成升序數列的操作次數是多少。

然後自然就會想到逆序對。每次變換之後逆序對的個數最多只能-1;

所以答案就是數列中逆序對的個數。

然後就是求逆序對啦。

逆序對有很多種做法:

TOP1:暴力O(N^2)時間TLE BOOM。。。!

TOP2:歸並排序

TOP3:線段樹

TOP4:樹狀數組(就是我寫的)(又短又好寫233~(PS:其實是其他的不會。。))

好吧。

其實我樹狀數組寫的時候也不會,現學的。。

順便講講樹狀數組的幾個基本操作;

NUM1: update

代碼如下:

void update(int x)
{
    while(x<=n)
    {
        d[x]++;
        x+=lowbit(x);    
    }    
}

這就相當於給樹狀數組賦值/(+/-一個值)

在此lowbit就是x在二進制下的第一個1的位置。

實在看不懂可以找規律: 比如x=1時,n=8時,我們要賦值的數組為d[1],d[2],d[4],d[8];

當x=3時,我們要賦值的數組為d[3],d[4],d[8];

x=5:d[5],d[6],d[8];

恩,就是這樣。

NUM2:getsum(相當於查詢)

下面貼代碼

int getsum(int x)
{
    int ans=0;
    while(x>0)
    {
        ans+=d[x];
        x-=lowbit(x);    
    }
    return ans;
}

相當於上一個查詢順序反過來(好像語序有問題。。。)

然後呢,這題就用這2個函數來求逆序對的個數。

在本代碼中getsum(x)求的是x之前<=x的數的數量。

這樣i-getsum(x)就是x之前>x的數的數量

即對於x的逆序對個數

最後累加一下,就是答案啦!

註意!在本代碼中,c數組用於離散化,防止爆棧,同時也加快效率。

d[i]表示在c[i-lowbit(i)]到c[i]中<=c[i]的數出現的總次數!(重點!這個點我理解了很久,要多多消化。也許是因為我蒟蒻。。。)

好吧,下面貼代碼

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int c[100001];
struct lisan{
    int value,opt;
}a[100001],b[100001];
int cmp(lisan a,lisan b){return a.value<b.value;}
int d[100001];
int n;
int lowbit(int x){return x&(-x);}
void update(int x)
{
    while(x<=n)
    {
        d[x]++;
        x+=lowbit(x);    
    }    
}
int getsum(int x)
{
    int ans=0;
    while(x>0)
    {
        ans+=d[x];
        x-=lowbit(x);    
    }
    return ans;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
    scanf("%d",&a[i].value);
    a[i].opt=i;
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&b[i].value);
        b[i].opt=i;
    }
    sort(a+1,a+n+1,cmp);
    sort(b+1,b+n+1,cmp);
    for(int i=1;i<=n;i++)
    c[b[i].opt]=a[i].opt;
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        update(c[i]);
        ans+=i-getsum(c[i]);
        ans=ans%99999997;
    }
    printf("%d\n",ans);
}

祝大家編程愉快啦233~(反正我理解了1個多小時。。)

然後狠狠的被CLZ,QRC等一群學弟和zxyerD了一頓。。

火柴排隊(NOIP2013)(附樹狀數組專題講解(其實只是粗略。。。))