三维偏序(陌上花开)
Posted tian-luo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了三维偏序(陌上花开)相关的知识,希望对你有一定的参考价值。
三维偏序(陌上花开)
题目背景
这是一道模板题
可以使用bitset,CDQ分治,K-DTree等方式解决。
题目描述
有 n 个元素,第 i 个元素有 ai?、bi?、ci? 三个属性,设 f(i) 表示满足 aj?≤ai? 且 bj?≤bi? 且 cj?≤ci? 的 j 的数量。
对于 d∈[0,n),求 f(i)=d 的数量
输入输出格式
输入格式:
第一行两个整数 n、k,分别表示元素数量和最大属性值。
之后 n 行,每行三个整数 ai?、bi?、ci,分别表示三个属性值。
输出格式:
输出 n 行,第 d+1 行表示 f(i)=d 的 i 的数量。
输入输出样例
说明
1≤n≤100000,1≤k≤200000
题目链接:https://www.luogu.org/problemnew/show/P3810
CDQ分治,首先按对原序列进行三维排序,然后运用分治的思想,分治过程向下的时候保证a有序,分治过程回溯的时候保证b有序,某一分治中间态,我们只考虑mid左边对mid右边的影响(同在左边或者同在右边通过分治解决),因为mid左边的a一定小于等于mid右边的a,然后我们按照归并排序的方法使得b也有序,这样对于mid右边的数来说,当前中间态有且仅有左边的且b小于等于该数的数才能对它有贡献,于是可以维护一个树状数组,在对b进行归并排序的过程中,统计左边的c出现的次数,并计算左边的数对右边的数的贡献。
小细节:若两个数的a,b,c值都相同,那么如果直接进行CDQ分治会导致相同数的贡献不能全部计算的情况,因为CDQ分治保证了每次计算左边的数对右边的数的贡献,但是相同的数无论左右都对彼此有贡献。所以要先去重,然后用一个权值代表该数出现了多少次。
#include<bits/stdc++.h> using namespace std; const int N = 2e5+50; struct ss int a,b,c,value,ans; bool operator < (const ss &s)const if(a!=s.a)return a<s.a; if(b!=s.b)return b<s.b; return c<s.c; bool operator == (const ss &s)const return (a==s.a&&b==s.b&&c==s.c); ; ss arr[N],ls[N]; pair<int,int> ls2[N]; int c[N]; void update(int pos,int v) for(int i=pos; i<N; i+=i&(-i))c[i]+=v; int Sum(int pos) int ans=0; for(int i=pos; i>0; i-=i&(-i))ans+=c[i]; return ans; void print(int l,int r) printf("%d %d:\n",l,r); for(int i=l; i<=r; i++)printf("%d %d %d %d\n",arr[i].a,arr[i].b,arr[i].c,arr[i].ans); printf("\n"); void cdq(int l,int r) //分治 if(l==r)return; int mid=(l+r)/2; cdq(l,mid); //先解决子问题 cdq(mid+1,r); int sum_ls2=0; int i=l,j=mid+1,k=l; //print(l,r); while(i<=mid&&j<=r) if(arr[i].b<=arr[j].b)//如果左边的数的b比较小,就更新树状数组,以便后边统计答案 update(arr[i].c,arr[i].value); ls2[sum_ls2++]= pair<int,int>(arr[i].c,arr[i].value); ls[k++]=arr[i++]; else//右边的数的b值比较小,那么此时左边的剩余的数的b值一定都大于该数的b值,则此时对该数有贡献的- //-数一定都是前面加到树状数组里的数 arr[j].ans+=Sum(arr[j].c); ls[k++]=arr[j++]; while(i<=mid) ls[k++]=arr[i++]; while(j<=r) arr[j].ans+=Sum(arr[j].c); ls[k++]=arr[j++]; for(int i=0; i<sum_ls2; i++)update(ls2[i].first,-ls2[i].second);//不能memset清空树状数组 for(int i=l; i<=r; i++)arr[i]=ls[i]; //print(l,r); int ans[N]= 0; int main() int n,k; scanf("%d %d",&n,&k); for(int i=1; i<=n; i++)scanf("%d %d %d",&arr[i].a,&arr[i].b,&arr[i].c),arr[i].ans=0; sort(arr+1,arr+1+n); int pos=1; arr[1].value=1; for(int i=2; i<=n; i++) //去重 if(arr[i]==arr[i-1]) arr[pos].value++; else arr[++pos]=arr[i]; arr[pos].value=1; // for(int i=1;i<=pos;i++)printf("%d %d %d %d\n",arr[i].a,arr[i].b,arr[i].c,arr[i].value); cdq(1,pos); for(int i=1; i<=pos; i++) arr[i].ans+=arr[i].value-1; //加上相同数的贡献 ans[arr[i].ans]+=arr[i].value; for(int i=0; i<n; i++)printf("%d\n",ans[i]); return 0;
以上是关于三维偏序(陌上花开)的主要内容,如果未能解决你的问题,请参考以下文章