CDQ分治——BZOJ3295/Luogu3157 [CQOI2011]动态逆序对

来源:互联网 发布:java线程池使用实例 编辑:程序博客网 时间:2024/06/03 12:29

BZOJ传送门
Luogu传送门
以前一直想用树套树过掉此题,太懒不想写。。。(我不会告诉你我其实是不会树套树做法因为实在太恶心了。。。
这里写图片描述
然后呢,现在掌握了新技能以后发现这题就可以很快地过去了
我们可以把删除操作看成倒着加入操作,然后。。。
这题就变成了时间为t时的逆序对数目,我们可以看成一个三维偏序,即a[i].t<=a[j].t,a[i].x < a[j].x,a[i].v > a[j].v
(t表示加入时间,x表示位置,v表示值)
所以就可以用CDQ大法了!!!
首先按照t把小于等于mid的移到左边去,右边同理
然后计算左边对右边的影响
即对于处于右边的第i个位置,计算逆序对数目
这个逆序对可以这么统计,先找位置比它小但值比它大的,再找位置比它大但值比它小的
可以调用两次树状数组实现
还是一样,最后清空树状数组,统计答案时累加即可

#include<bits/stdc++.h>using namespace std;typedef long long ll;struct ppap{int x,v,t;}a[100001],b[100001];int p[100001],n,m;ll ans[100001],f[100001],q[100001],h[100001];inline int lowbit(int x){return x&-x;}inline void add(int x,int v){for(;x<=n;x+=lowbit(x))f[x]+=v;}inline int sum(int x){int ans=0;for(;x;x-=lowbit(x))ans+=f[x];return ans;}void cdq(int l,int r){    if(l==r)return;    int mid=l+r>>1,b1=l-1,b2=mid;    for(int i=l;i<=r;i++)if(a[i].t<=mid)b[++b1]=a[i];    else b[++b2]=a[i];    for(int i=l;i<=r;i++)a[i]=b[i];    int cc=l;    for(int i=mid+1;i<=r;i++){        for(;cc<=mid&&a[cc].x<a[i].x;cc++)add(a[cc].v,1);        q[a[i].t]+=(ll)cc-l-sum(a[i].v);    }    for(int i=l;i<cc;i++)add(a[i].v,-1);    cc=mid;    for(int i=r;i>mid;i--){        for(;cc>=l&&a[cc].x>a[i].x;cc--)add(a[cc].v,1);        h[a[i].t]+=(ll)sum(a[i].v);    }    for(int i=mid;i>cc;i--)add(a[i].v,-1);    cdq(l,mid);cdq(mid+1,r);}int main(){    scanf("%d%d",&n,&m);int x;    for(int i=1;i<=n;i++)a[i].x=i,scanf("%d",&a[i].v),p[a[i].v]=i;    for(int i=n;i>n-m;i--)scanf("%d",&x),a[p[x]].t=i;    int k=n-m;    for(int i=1;i<=n;i++)if(!a[i].t)a[i].t=k--;    cdq(1,n);    for(int i=1;i<=n;i++)ans[i]=ans[i-1]+q[i]+h[i];    for(int i=n;i>n-m;i--)printf("%lld\n",ans[i]);    return 0;}
1 0
原创粉丝点击