bzoj 3295: [Cqoi2011]动态逆序对 cdq分治+树状数组

来源:互联网 发布:软件开发是什么专业 编辑:程序博客网 时间:2024/05/16 09:39

题目大意

给出1-n的一个排列和m个操作,每个操作有一个数字x表示在序列中把x这个数删掉,并且求在删掉x前该序列中有多少个逆序对。
n<=100000,m<=50000

分析

这题看完题后就想到了可以用树套树来搞,但是比较麻烦(其实也不是很麻烦)并且对于n<=100000,m<=50000的数据范围如果用nlog^2的复杂度去做的话会跑的很慢,于是就选择了新学的cdq分治。

先一开始往序列里面插入没被删除的数,然后从后往前的顺序处理操作。把每个操作拆分成两个操作,一个是插入,一个是询问,顺序不限。
那么问题就转换成了对于三元组(x,y,z)的处理,x是操作序号,y是下标,z是具体数值。对于这种问题一般的处理方式都是第一维直接排序,第二维cdq分治掉,第三维树状数组。
具体的就跟bzoj 3262一样啦。

注意要开long long

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#define N 100005#define ll long longusing namespace std;int c[N],a[N],pos[N],n,m,cnt,tot,vis[N],num[N],ans[N];struct data{int x,pos,op,id;}q[N*10],t[N*10];void ins(int x,int y){    while (x<=n)    {        c[x]+=y;        x+=x&(-x);    }}int query(int x){    int ans=0;    while (x)    {        ans+=c[x];        x-=x&(-x);    }    return ans;}void cdq(int l,int r){    if (l>=r) return;    int mid=(l+r)/2;    cdq(l,mid);cdq(mid+1,r);    for (int j=mid+1,i=l;j<=r;j++)    {        while (q[i].pos<q[j].pos&&i<=mid)        {            if (q[i].op==1) ins(q[i].x,1);            i++;        }        if (q[j].op==2) ans[q[j].id]+=query(q[j].x-1);    }    for (int i=l,j=mid+1;j<=r;j++)        while (q[i].pos<q[j].pos&&i<=mid)        {            if (q[i].op==1) ins(q[i].x,-1);            i++;        }    for (int i=mid,j=r;j>mid;j--)    {        while (q[i].pos>q[j].pos&&i>=l)        {            if (q[i].op==1) ins(q[i].x,1);            i--;        }        if (q[j].op==2) ans[q[j].id]+=query(n)-query(q[j].x);    }    for (int i=mid,j=r;j>mid;j--)        while (q[i].pos>q[j].pos&&i>=l)        {            if (q[i].op==1) ins(q[i].x,-1);            i--;        }    int i=l,j=mid+1,k=l;    while (i<=mid||j<=r)        if (q[i].pos<q[j].pos&&i<=mid||j>r) t[k++]=q[i++];        else t[k++]=q[j++];    for (int i=l;i<=r;i++)        q[i]=t[i];}int main(){    scanf("%d%d",&n,&m);    for (int i=1;i<=n;i++)    {        scanf("%d",&a[i]);        pos[a[i]]=i;    }    for (int i=1;i<=m;i++)    {        scanf("%d",&num[i]);        vis[pos[num[i]]]=1;    }    ll sum=0;    for (int i=1;i<=n;i++)    {        sum+=query(n)-query(a[i]);        ins(a[i],1);    }    for (int i=1;i<=n;i++)        ins(a[i],-1);    for (int i=1;i<=n;i++)        if (!vis[i])        {            q[++tot].op=1;            q[tot].pos=i;            q[tot].x=n-a[i]+1;        }    for (int i=m;i>=1;i--)    {        q[++tot].op=1;        q[tot].pos=pos[num[i]];        q[tot].x=n-num[i]+1;        q[++tot].op=2;        q[tot].pos=pos[num[i]];        q[tot].x=n-num[i]+1;        q[tot].id=++cnt;    }    cdq(1,tot);    for (int i=cnt;i>=1;i--)    {        printf("%lld\n",sum);        sum-=ans[i];    }    return 0;}
0 0
原创粉丝点击