BZOJ3295:[Cqoi2011]动态逆序对 (BIT套treap/CDQ分治+BIT)

来源:互联网 发布:linux宕机日志 编辑:程序博客网 时间:2024/06/07 14:36

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3295


题目分析:这题裸的树套树啊。我们先算出原序列的逆序对数量,当一个数被删除的时候,逆序对的减小量=此时在它前面的比它大的数的个数+此时在它后面的比它小的数的个数。于是我们用一个树状数组套平衡树就可以搞定了。


CODE(BIT+treap):

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=100100;const int lg=20;const long long M=1000000009;const long long Times=998244353;typedef long long LL;struct Tnode{    int val,fix,Size;    Tnode *lson,*rson;    int Lsize() { return (lson? lson->Size:0); }    int Rsize() { return (rson? rson->Size:0); }    void Get_size() { Size=Lsize()+Rsize()+1; }} tree[maxn*lg];Tnode *bit[maxn];int cur=-1;LL seed,ans=0;int a[maxn];int p[maxn];int n,m;int Rand(){    seed=seed*Times%M;    return (int)seed;}Tnode *New_node(int v){    cur++;    tree[cur].val=v;    tree[cur].fix=Rand();    tree[cur].Size=1;    tree[cur].lson=tree[cur].rson=NULL;    return tree+cur;}void Right_turn(Tnode *&P){    Tnode *W=P->lson;    P->lson=W->rson;    W->rson=P;    P=W;    P->rson->Get_size();    P->Get_size();}void Left_turn(Tnode *&P){    Tnode *W=P->rson;    P->rson=W->lson;    W->lson=P;    P=W;    P->lson->Get_size();    P->Get_size();}void Insert(Tnode *&P,int v){    if (!P) P=New_node(v);    else    {        if ( v<P->val )        {            Insert(P->lson,v);            if ( P->lson->fix<P->fix ) Right_turn(P);        }        else        {            Insert(P->rson,v);            if ( P->rson->fix<P->fix ) Left_turn(P);        }        P->Get_size();    }}void Add(int x,int v){    while (x<=n)    {        Insert(bit[x],v);        x+=(x&(-x));    }}int Query(Tnode *P,int v){    if (!P) return 0;    if ( P->val<v ) return P->Lsize()+1+Query(P->rson,v);    return Query(P->lson,v);}int Sum(int x,int v,bool f){    int sum=0;    while (x)    {        int temp=Query(bit[x],v);        if ( f && bit[x] ) temp=bit[x]->Size-temp;        sum+=temp;        x-=(x&(-x));    }    return sum;}void Delete(Tnode *&P,int v){    if ( P->val==v )        if ( !P->lson )            if ( !P->rson ) P=NULL;            else P=P->rson;        else            if ( !P->rson ) P=P->lson;            else                if ( P->lson->fix<P->rson->fix )                    Right_turn(P),Delete(P->rson,v),P->Get_size();                else Left_turn(P),Delete(P->lson,v),P->Get_size();    else        if ( v<P->val ) Delete(P->lson,v),P->Get_size();        else Delete(P->rson,v),P->Get_size();}void Dec(int x,int v){    while (x<=n)    {        Delete(bit[x],v);        x+=(x&(-x));    }}int main(){    freopen("3295.in","r",stdin);    freopen("3295.out","w",stdout);    scanf("%d%d",&n,&m);    for (int i=1; i<=n; i++) scanf("%d",&a[i]),p[ a[i] ]=i,bit[i]=NULL;    seed=p[1];    for (int i=1; i<=n; i++) Add(i,a[i]),ans+=(long long)( Sum(i-1,a[i],1) );    for (int i=1; i<=m; i++)    {        printf("%lld\n",ans);        int x;        scanf("%d",&x);        Dec(p[x],x);        ans-=Sum(p[x]-1,x,1);        ans-=( Sum(n,x,0)-Sum(p[x],x,0) );    }    return 0;}

当然,我们也可以用CDQ分治。如果我们将此题的操作序列抽象出来,大概就是这样:QDQDQDQD,其中Q表示查询,D表示删数。我们记nx[i]为逆序对中有i这个值的逆序对数,则原数列的逆序对总数为sum=ni=1nx[i]2。一开始令每一个查询答案都为sum,令每一个删除x的操作都会使逆序对数减少nx[x]。然后我们将操作序列分为两半,很明显左半部分的D会对右半部分的Q与D都产生影响,所以我们应先递归左半部分,知道每一个D实际上删除了多少个逆序对。然后将右边的Q的答案减去左边的D删除的逆序对数,并将左边的D和右边的D堆在一起排一次序,用树状数组记录左边Delete的数会对右边的D所删除的逆序对数产生怎样的影响。时间复杂度O(nlog2(n))

CODE:

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=100100;const int maxm=maxn>>1;typedef long long LL;struct data{    int id;    bool Left;} b[maxn];int work[maxn];LL num[maxn];int bit[maxn];LL nx[maxn];LL sum=0;int a[maxn];int p[maxn];int n,m;int Sum(int x){    int temp=0;    while (x)    {        temp+=bit[x];        x-=(x&(-x));    }    return temp;}void Add(int x,int v){    while (x<=n)    {        bit[x]+=v;        x+=(x&(-x));    }}bool Comp1(data x,data y){    return p[ work[ x.id ] ]<p[ work[ y.id ] ];}bool Comp2(data x,data y){    return p[ work[ x.id ] ]>p[ work[ y.id ] ];}void CDQ(int L,int R){    if (L==R) return;    int mid=(L+R)>>1;    CDQ(L,mid);    int temp=0;    LL dec=0;    for (int i=L; i<=mid; i++)        if (work[i])        {            temp++;            b[temp].id=i;            b[temp].Left=true;            dec+=num[i];        }    for (int i=mid+1; i<=R; i++)        if (work[i])        {            temp++;            b[temp].id=i;            b[temp].Left=false;        }        else num[i]-=dec;    sort(b+1,b+temp+1,Comp1);    int k=0;    for (int i=1; i<=temp; i++)    {        int j=b[i].id;        if (!b[i].Left) num[j]-=(k-Sum(work[j]));        else k++,Add(work[j],1);    }    for (int i=1; i<=temp; i++)        if (b[i].Left) Add(work[ b[i].id ],-1);    sort(b+1,b+temp+1,Comp2);    for (int i=1; i<=temp; i++)    {        int j=b[i].id;        if (!b[i].Left) num[j]-=Sum(work[j]-1);        else Add(work[j],1);    }    for (int i=1; i<=temp; i++)        if (b[i].Left) Add(work[ b[i].id ],-1);    CDQ(mid+1,R);}int main(){    freopen("3295.in","r",stdin);    freopen("3295.out","w",stdout);    scanf("%d%d",&n,&m);    for (int i=1; i<=n; i++)    {        scanf("%d",&a[i]);        p[ a[i] ]=i;        nx[i]=(i-1)-Sum(a[i]);        Add(a[i],1);    }    for (int i=1; i<=n; i++) bit[i]=0;    for (int i=n; i>=1; i--)    {        nx[i]+=Sum(a[i]-1);        Add(a[i],1);    }    for (int i=1; i<=n; i++) bit[i]=0,sum+=nx[i];    sum>>=1;    for (int i=1; i<=m; i++)    {        int x=(i<<1)-1;        num[x]=sum;        x++;        scanf("%d",&work[x]);        num[x]=nx[ p[ work[x] ] ];    }    CDQ(1,m<<1);    for (int i=1; i<(m<<1); i+=2) printf("%lld\n",num[i]);    return 0;}
阅读全文
1 0
原创粉丝点击