[BZOJ3295][Cqoi2011]动态逆序对 CDQ分治&树套树
Posted LadyLex
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[BZOJ3295][Cqoi2011]动态逆序对 CDQ分治&树套树相关的知识,希望对你有一定的参考价值。
3295: [Cqoi2011]动态逆序对
Time Limit: 10 Sec Memory Limit: 128 MBDescription
Input
Output
Sample Input
1
5
3
4
2
5
1
4
2
Sample Output
2
2
1
样例解释
(1,5,3,4,2) (1,3,4,2) (3,4,2) (3,2) (3)。
HINT
N<=100000 M<=50000
题解:
啊啊啊这道题真的是差点把自己打死
跟zyfdalao分享了一下思路然后他就按我的思路A了可是我自己却不会了
如果你是树套树玩家,请移步:http://www.cnblogs.com/TSHugh/p/7001884.html
在具体写题解之前,我要分享一个面对瓶颈很好的做法(我这样反复理了三遍,思路一遍比一遍清晰...遇到瓶颈的时候真的很推荐这样做):
思路不通了把所有已知条件再列出来一边,写清变量之间的关系和限制,并且针对每一个条件写出解决方案和打法
感觉凌乱的代码段就删掉重打一遍,用的总时间一定会比死调下去少
首先一个小优化:把val变成n-val+1,这样原来的逆序对就变成了现在的顺序对,个人感觉更加好操作.
我们考虑,我们可以利用树状数组求出一开始总的逆序对数,并且同时利用树状数组求出以每个位置为开头和结尾的逆序对个数,
我们设num数组表示这个数目,num[0][i]表示以i位置结尾的逆序对个数,num[1][i]表示以i位置开头的逆序对个数.
这样的话,每次我们先输出当前的总数,再减去num[0][i]+num[1][i],然后...
我们发现有一些不对,如果原来形成的逆序对的另外那个数已经被删除了,那么这一对就不应该被减去
所以我们考虑再计算一个delta表示这个增量:
其中delta[0][i]表示以i位置结尾,但是另外那个数比i删除的早的逆序对个数,
delta[1][i]表示以i位置开头,但是另外那个数比i删除的早的逆序对个数.
接下来我们考虑什么样的数对(i,j)会被记入delta
如果设i位置的数的删除时间是tim[i](没有被删除的数的删除时间顺次设成m+1~n即可,他们的delta不会被记入答案)
设i位置的数值是val[i](这里的val已经进行了n-val+1取反操作)
那么delta[0][i]里的数应该满足:
tim[i]>tim[j]
val[i]>val[j]
i>j
delta[1][i]里的数应该满足:
tim[i]>tim[j]
val[i]<val[j]
i<j
那么上面这两组约束条件(尤其是第一组)很像一个三维偏序的统计问题.
的确如此,我们只需要用CDQ分治统计一下符合条件的数对数即可.
拿我的思路来说,我们可以让时间有序,给下标排序,用树状数组维护权值val,
这样跑两遍CDQ统计符合条件的偏序对数,就能分别计算delta[0][i]和delta[1][i]
最后输出答案时,先输出答案ans,然后给ans减去num[0][i]+num[1][i],再加上delta[0][i]+delta[1][i],这样就能正确的维护逆序对数了,
代码实现:
1 #include <cstring> 2 #include <cstdio> 3 #include <algorithm> 4 using namespace std; 5 typedef long long LL; 6 const int N=100010,M=50010; 7 struct node 8 { 9 int tim,val,pos; 10 node (int a=0,int b=0,int c=0){tim=a,val=b,pos=c;} 11 }q[N]; 12 int cnt,n,m,a[N],match[N],step[M]; 13 bool vis[N]; 14 LL delta[2][N],num[2][N],bit[N]; 15 inline int lowbit(int a){return a&-a;} 16 inline void add(int a,LL b) 17 {while(a<=n)bit[a]+=b,a+=lowbit(a);} 18 inline LL sum(int a) 19 {LL ret=0;while(a)ret+=bit[a],a-=lowbit(a);return ret;} 20 inline void gsum0() 21 { 22 for(register int i=1;i<=n;++i) 23 num[0][i]=sum(a[i]),add(a[i],1); 24 memset(bit,0,sizeof(bit)); 25 } 26 inline void gsum1() 27 { 28 for(register int i=n;i;--i) 29 num[1][i]=sum(a[i]),add(1,1),add(a[i],-1); 30 memset(bit,0,sizeof(bit)); 31 } 32 inline void readin() 33 { 34 register int i;scanf("%d%d",&n,&m); 35 for(i=1;i<=n;++i) 36 scanf("%d",&a[i]),a[i]=n-a[i]+1,match[a[i]]=i; 37 gsum0();gsum1(); 38 for(i=1;i<=m;++i) 39 scanf("%d",&step[i]),step[i]=n-step[i]+1,vis[step[i]]=1; 40 } 41 inline bool mt1(const node &a,const node &b){return a.pos<b.pos;} 42 inline bool mt2(const node &a,const node &b){return a.tim<b.tim;} 43 inline void CDQ0(int l,int r) 44 { 45 if(l==r)return; 46 register int mi=l+r>>1,i; 47 CDQ0(l,mi);CDQ0(mi+1,r); 48 sort(q+l,q+r+1,mt1); 49 for(i=l;i<=r;++i) 50 if(q[i].tim<=mi)add(q[i].val,1); 51 else delta[0][q[i].tim]+=sum(q[i].val); 52 for(i=l;i<=r;++i) 53 if(q[i].tim<=mi)add(q[i].val,-1); 54 } 55 inline void CDQ1(int l,int r) 56 { 57 if(l==r)return; 58 register int mi=l+r>>1,i; 59 CDQ1(l,mi);CDQ1(mi+1,r); 60 sort(q+l,q+r+1,mt1); 61 for(i=r;i>=l;--i) 62 if(q[i].tim<=mi)add(1,1),add(q[i].val,-1); 63 else delta[1][q[i].tim]+=sum(q[i].val); 64 for(i=r;i>=l;--i) 65 if(q[i].tim<=mi)add(1,-1),add(q[i].val,1); 66 } 67 int main() 68 { 69 register int i;readin(); 70 for(i=1;i<=m;++i) 71 q[++cnt]=node(cnt,step[i],match[step[i]]); 72 for(i=1;i<=n;++i) 73 if(!vis[a[i]])q[++cnt]=node(cnt,a[i],i); 74 CDQ0(1,n),sort(q+1,q+n+1,mt2),CDQ1(1,n); 75 LL ans=0;int pos; 76 for(i=1;i<=n;++i)ans+=num[0][i]; 77 for(i=1;i<=m;++i) 78 { 79 printf("%lld\\n",ans),pos=match[step[i]], 80 ans-=(num[0][pos]+num[1][pos]), 81 ans+=(delta[0][i]+delta[1][i]); 82 } 83 }
以上是关于[BZOJ3295][Cqoi2011]动态逆序对 CDQ分治&树套树的主要内容,如果未能解决你的问题,请参考以下文章