bzoj3295: [Cqoi2011]动态逆序对(cdq分治)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 #define maxn 200001 7 using namespace std; 8 9 typedef long long ll; 10 ll ans[maxn],Ans; 11 int n,m,tot,tsum[maxn],num[maxn],pos[maxn],sum[maxn],_num[maxn]; 12 bool bo[maxn]; 13 14 struct date{ 15 int bo,id,x,y; 16 }qs[maxn],temp[maxn]; 17 18 int lowbit(int x){ 19 return x&(-x); 20 } 21 22 void insert(int x,int y){ 23 for (int i=x;i<=n;i+=lowbit(i)) sum[i]+=y; 24 } 25 26 int qsum(int x){ 27 int tmp=0; for (int i=x;i;i-=lowbit(i)) tmp+=sum[i]; 28 return tmp; 29 } 30 31 void tinsert(int x){ 32 for (int i=x;i<=n;i+=lowbit(i)) tsum[i]++; 33 } 34 35 int tqsum(int x){ 36 int tmp=0; 37 for (int i=x;i>0;i-=lowbit(i)){ 38 tmp+=tsum[i]; 39 } 40 return tmp; 41 } 42 43 bool comp(date x,date y){ 44 return x.x<y.x; 45 } 46 47 bool comp2(date x,date y){ 48 return x.id<y.id; 49 } 50 51 void cdq_solve(int l,int r){ 52 if (l==r) return; 53 int mid=(l+r)/2,tmp=0; cdq_solve(l,mid),cdq_solve(mid+1,r); 54 sort(qs+l,qs+mid+1,comp),sort(qs+mid+1,qs+r+1,comp); 55 for (int i=l,j=mid+1;j<=r;){ 56 for (;i<=mid&&qs[i].bo==2;i++); 57 for (;j<=r&&qs[j].bo==1;j++); 58 if (j>r) break; 59 if (qs[i].x<qs[j].x&&i<=mid) insert(qs[i].y,1),tmp=i++; 60 else ans[qs[j].id]+=qsum(qs[j].y-1),j++; 61 } 62 for (int i=l;i<=tmp;i++) if (qs[i].bo==1) insert(qs[i].y,-1); 63 } 64 65 int main(){ 66 // freopen("dtnxd.in","r",stdin); 67 // freopen("dtnxd.out","w",stdout); 68 int u,v; 69 memset(sum,0,sizeof(sum)); 70 memset(tsum,0,sizeof(tsum)),Ans=0; 71 memset(bo,0,sizeof(bo)); 72 memset(ans,0,sizeof(ans)); 73 scanf("%d%d",&n,&m),tot=0; 74 for (int i=1;i<=n;i++) scanf("%d",&u),pos[u]=i,_num[i]=u; 75 for (int i=m;i>=1;i--) scanf("%d",&num[i]),bo[pos[num[i]]]=1; 76 for (int i=n;i>=1;i--){ 77 if (bo[i]==0){ 78 Ans+=tqsum(_num[i]-1); 79 tinsert(_num[i]); 80 } 81 } 82 for (int i=1;i<=n;i++){ 83 if (!bo[i]) u=_num[i],++tot,qs[tot].bo=1,qs[tot].x=u,qs[tot].y=i,qs[tot].id=tot; 84 } 85 for (int i=1;i<=m;i++){ 86 u=num[i],v=pos[u]; 87 qs[++tot].bo=1,qs[tot].x=u,qs[tot].y=v,qs[tot].id=tot; 88 qs[++tot].bo=2,qs[tot].x=u,qs[tot].y=v,qs[tot].id=tot; 89 } 90 // for (int i=1;i<=tot;i++) printf("%d %d %d %d\n",qs[i].x,qs[i].y,qs[i].bo,qs[i].id); 91 for (int i=1;i<=tot;i++){ 92 temp[i]=qs[i]; 93 qs[i].x=n+1-qs[i].x; 94 } 95 cdq_solve(1,tot); 96 for (int i=1;i<=tot;i++){ 97 qs[i]=temp[i]; 98 qs[i].y=n+1-qs[i].y; 99 } 100 cdq_solve(1,tot); 101 sort(qs+1,qs+tot+1,comp2); 102 // for (int i=1;i<=tot;i++) printf("%d %lld\n",i,ans[i]); 103 for (int i=1;i<=tot;i++) ans[i]+=ans[i-1]; 104 for (int i=tot;i>0;i--) if (qs[i].bo==2) printf("%lld\n",ans[i]+Ans); 105 return 0; 106 }
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3295
题目大意:见上一篇博客。
做法:上一篇博客中,我介绍了树套树的做法,现在我来讲讲cdq分治的做法。
求逆序对的常用做法除了用树状数组维护以外,还可以用归并排序求,其本质是cdq分治。
cdq分治做法:
题目中是删除一些数,我们可以离线,看作是往序列中不断地加入一些数每加入一个数字,我们考虑它对答案带来的影响,它对答案的影响就是ans+=目前的序列中排在它前面的比它大的数的个数+排在它后面的比它小的数的个数。简化之后就是给定若干个三元组(x,y,z),x就是题目中操作的顺序(稍微调整一下即可),y表示这个数的权值,z表示这个数的位置,这些三元组中有些是询问,有些是修改,对于询问,就是求修改中x比它的X小的、y比它的Y大的、z比它的小的个数+修改中x比它的X小的、y比它的小的、z比它的大的个数,对于这种三维偏序问题,我们考虑cdq分治,第一维分治外层,第二维排序,第三位树状数组维护即可。
cdq分治+树状数组