在solve(L,R)中,需要先分治solve两个子区间,再计算左边区间修改对右边区间询问的贡献。
注意,计算额外的贡献时,两子区间各自内部的顺序变得不再重要(不管怎么样左边区间的都发生在右边之前),于是就少了一维
https://www.lydsy.com/JudgeOnline/problem.php?id=3262
https://www.luogu.org/problemnew/show/P3810
此题每个操作既是修改又是查询
对于此题,先按一维排序,在solve(L,R)中先solve两个子区间,然后把L到R的操作按二维排序(由于cdq分治类似归并的特性此时两个子区间内部二维都是有序的,可以直接二路归并),然后就是一个对二、三维求逆序对的过程(只不过只有归并前在第一个区间内的修改生效,归并前在第二个区间内的查询要更新答案)
可以记一下每个元素在按第一维排序后的编号(以下代码中q[i].num),来判断它归并前是哪个区间里的
注意:此题第一维相同的实际并不存在顺序关系,理应同时处理然后同时计算贡献,但排序后它们间总是要存在一个特定顺序的,所以要加一些奇怪的特判
具体的话:首先一开始排序的时候三个关键字都要依次考虑(而不是只考虑第一维),这样可以保证排序后大部分情况下后面的不会对前面产生贡献
上面还漏考虑了完全相等的三元组,如果它们存在则后面也会对前面产生贡献。因此只要在开始solve前补上这些后面对前面产生的贡献即可
归并可以简化为
merge(q+lp,q+mid+1,q+mid+1,q+rp+1,tmp+lp); copy(tmp+lp,tmp+rp+1,q+lp);
或
inplace_merge(q+lp,q+mid+1,q+rp+1);
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 struct Q 5 { 6 int a,b,c,ans,num; 7 }q[100100],tmp[100100]; 8 int n,k; 9 bool c1(const Q &a,const Q &b) {return a.a<b.a||(a.a==b.a&&a.b<b.b)||(a.a==b.a&&a.b==b.b&&a.c<b.c);} 10 bool operator<(const Q &a,const Q &b) {return a.b<b.b||(a.b==b.b&&a.num<b.num);} 11 bool operator==(const Q &a,const Q &b) {return a.a==b.a&&a.b==b.b&&a.c==b.c;} 12 int dat[200100]; 13 const int N=200000; 14 #define lowbit(x) ((x)&(-x)) 15 void addx(int pos,int d) 16 { 17 for(;pos<=N;pos+=lowbit(pos)) dat[pos]+=d; 18 } 19 int sum(int pos) 20 { 21 int ans=0; 22 for(;pos>0;pos-=lowbit(pos)) ans+=dat[pos]; 23 return ans; 24 } 25 int num[100100]; 26 void solve(int lp,int rp) 27 { 28 if(lp==rp) return; 29 int mid=lp+(rp-lp)/2; 30 solve(lp,mid);solve(mid+1,rp); 31 int k=lp-1,i,j; 32 for(i=lp,j=mid+1;i<=mid&&j<=rp;) 33 { 34 ++k; 35 if(q[i]<q[j]) tmp[k]=q[i++]; 36 else tmp[k]=q[j++]; 37 } 38 while(i<=mid) tmp[++k]=q[i++]; 39 while(j<=rp) tmp[++k]=q[j++]; 40 for(i=lp;i<=rp;i++) q[i]=tmp[i]; 41 for(i=lp;i<=rp;i++) 42 { 43 if(q[i].num<=mid) addx(q[i].c,1); 44 else q[i].ans+=sum(q[i].c); 45 } 46 for(i=lp;i<=rp;i++) 47 if(q[i].num<=mid) 48 addx(q[i].c,-1); 49 } 50 int main() 51 { 52 int i,j,t,tt=0; 53 scanf("%d%d",&n,&t); 54 for(i=1;i<=n;i++) scanf("%d%d%d",&q[i].a,&q[i].b,&q[i].c); 55 sort(q+1,q+n+1,c1); 56 for(i=1;i<=n;i++) 57 { 58 tt++; 59 if(i==n||!(q[i]==q[i+1])) 60 { 61 for(j=i-tt+1,t=tt-1;j<=i;j++) q[j].ans+=t,--t; 62 tt=0; 63 } 64 } 65 for(i=1;i<=n;i++) q[i].num=i; 66 solve(1,n); 67 for(i=1;i<=n;i++) num[q[i].ans]++; 68 for(i=0;i<n;i++) printf("%d ",num[i]); 69 return 0; 70 }
来看看cdq分治的限制
1.题目允许离线操作
2.修改操作对询问的贡献独立,且修改之间互不影响
3.修改对答案的贡献是确定的,与判定标准无关