【洛谷P3810 三维偏序】
题目描述:
有 n 个元素,第 i 个元素有 ai,bi,ci 三个属性,设 f(i,j) 表示满足 aj≤ai 且 bj≤bi 且 cj≤ci 的 j 的数量。
对于 d∈[0,n),求 f(i) = d 的数量。
输入格式:
第一行两个整数n,k分别表示元素数量和最大属性值。
之后 n 行,每行三个整数 ai、bi、ci,分别表示三个属性值。
输出格式:
输出 nn 行,第 d + 1d+1 行表示 f(i) = df(i)=d 的 ii 的数量。
输入样例:
10 3 3 3 3 2 3 3 2 3 1 3 1 1 3 1 2 1 3 1 1 1 2 1 2 2 1 3 2 1 2 1
输出样例:
3 1 3 0 1 0 1 0 0 1
题解:
cdq分治。(感觉细节多多)
我先说说如何理解cdq分治吧。(这东西可以省去树套树的一棵树)假装这时你左右两边都处理好了,开始合并。比如这题,如果左边的y<=右边的y,说明这个点对我右边当前询问有影响,所以先去bit维护添加z,那么当右边询问左边有多少个点满足时,bit维护添加的y都是小于当前y且全部添加,那么直接询问z这个值域的值即可。
如果问右边关于右边的答案有没有统计,就是归并的问题了。(归并过程顺便把y排序了,方便上面合并)
每个区间的bit是一次性的,所以要清空。(如果不清会重复计算)
1 #include<iostream> 2 #include<cstdlib> 3 #include<cstring> 4 #include<cstdio> 5 #include<algorithm> 6 using namespace std; 7 struct node{ 8 int x,y,z,num,sum; 9 } a[100005],t[100005],tt[100005]; 10 int cnt,ans[10000005]; 11 inline bool cmp(node X,node Y){ 12 if(X.x!=Y.x) return X.x<Y.x; 13 if(X.y!=Y.y) return X.y<Y.y; 14 return X.z<Y.z; 15 } 16 int n,K,bit[10000005]; 17 inline void add(int x,int k){ 18 for(int i=x;i<=K;i+=(i&-i)){ 19 bit[i]+=k; 20 } 21 } 22 inline int query(int x){ 23 int ret=0; 24 for(int i=x;i;i-=(i&-i)){ 25 ret+=bit[i]; 26 } 27 return ret; 28 } 29 inline void clean(int x){ 30 for(int i=x;i<=K;i+=(i&-i)){ 31 bit[i]=0; 32 } 33 } 34 inline void merge(int l,int r){ 35 int pos=0; 36 if(l==r) return; 37 int mid=(l+r)>>1; 38 merge(l,mid);merge(mid+1,r); 39 int L=l,R=mid+1; 40 while(R<=r){ 41 while(L<=mid && (t[L].y<t[R].y || (t[L].y==t[R].y && t[L].z<=t[R].z))){ 42 tt[++pos]=t[L]; 43 add(tt[pos].z,tt[pos].num);L++; 44 } 45 tt[++pos]=t[R]; 46 tt[pos].sum+=query(tt[pos].z); 47 R++; 48 } 49 for(int i=L;i<=mid;i++){ 50 tt[++pos]=t[i]; 51 } 52 pos=0; 53 for(int i=l;i<=r;i++){ 54 t[i]=tt[++pos]; 55 clean(tt[pos].z); 56 } 57 } 58 int main(){ 59 scanf("%d%d",&n,&K); 60 for(int i=1;i<=n;i++){ 61 scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z); 62 } 63 sort(a+1,a+n+1,cmp); 64 for(int i=1;i<=n;i++){ 65 if(a[i].x==a[i-1].x && a[i].y==a[i-1].y && a[i].z==a[i-1].z){ 66 t[cnt].num++; 67 } 68 else t[++cnt]=a[i],t[cnt].num++; 69 } 70 merge(1,cnt); 71 for(int i=1;i<=cnt;i++){ 72 ans[t[i].num+t[i].sum-1]+=t[i].num; 73 } 74 for(int i=0;i<n;i++){ 75 printf("%d\n",ans[i]); 76 } 77 return 0; 78 }