洛谷 P3810 三位偏序(CDQ分治+树状数组)
题目背景
这是一道模板题
可以使用bitset,CDQ分治,K-DTree等方式解决。
题目描述
有 nn 个元素,第 ii 个元素有 a_iai、b_ibi、c_ici 三个属性,设 f(i)f(i) 表示满足 a_j \leq a_iaj≤ai 且 b_j \leq b_ibj≤bi 且 c_j \leq c_icj≤ci 的 jj 的数量。
对于 d \in [0, n)d∈[0,n),求 f(i) = df(i)=d 的数量
输入输出格式
输入格式:
第一行两个整数 nn、kk,分别表示元素数量和最大属性值。
之后 nn 行,每行三个整数 a_iai、b_ibi、c_ici,分别表示三个属性值。
输出格式:
输出 nn 行,第 d + 1d+1 行表示 f(i) = df(i)=d 的 ii 的数量。
输入输出样例
输入样例#1: 复制
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
输出样例#1: 复制
3 1 3 0 1 0 1 0 0 1
说明
1 \leq n \leq 100000, 1 \leq k \leq 2000001≤n≤100000,1≤k≤200000
分析:
对于第一维的处理,用sort进行按a的大小排序,第二维的处理用CDQ分治,将总的区间二分成子区间,再将每个子区间分成左右两部分,再按B的大小排序,就能找到满足第二维的位置。第三维用树状数组进行处理,处理方法类似于树状数组对于逆序对的人处理,我们将c作为数组的下标加入到树状数组中,那么最后再统计sum(c)就是所有小于等于它的值的数量的总和。
需要注意的是,由于题目中找的是大于等于这样的关系,所以排序之后,可能前面的数等于后面的数,造成前面的数的贡献的不完全统计,所以我们让a,b,c都相等的点的值,都等于它们之中的最大值
这里用两个for进行最大值处理即可,具体见代码
代码如下:
// luogu-judger-enable-o2 //查询和修改都是log(n) //从c[1]开始进行赋值,不赋c[0],n为数的个数 //sum(x)为计算前x个数的和 #include <cstdio> #include <iostream> #include <cstring> #include <algorithm> using namespace std; int ans[100010]; int n,k; struct Bitree { int c[200010]; int lowbit(int x) { return x&(-x); } void add(int i,int value) { while(i<=k) { c[i]+=value; i+=lowbit(i); } } int sum(int i) { int sum=0; while(i>=1) { sum+=c[i]; i-=lowbit(i); } return sum; } }BIT; struct node { int a; int b; int c; int id; int sum; }poi[100010]; bool cmpb(node x,node y) { if(x.b!=y.b)return x.b<y.b; return x.id<y.id; } bool cmp(node x,node y) { if(x.a!=y.a)return x.a<y.a; if(x.b!=y.b)return x.b<y.b; if(x.c!=y.c)return x.c<y.c; } bool cmp2(node x,node y) { if(x.a!=y.a)return x.a<y.a; if(x.b!=y.b)return x.b<y.b; if(x.c!=y.c)return x.c<y.c; } void CDQ(int l,int r) { if(l==r)return; int mid=(l+r)>>1; CDQ(l,mid); CDQ(mid+1,r); sort(poi+l,poi+mid+1,cmpb); sort(poi+mid+1,poi+r+1,cmpb); int j=l; for(int i=mid+1;i<=r;i++) { for(;j<=mid&&poi[i].b>=poi[j].b;j++)BIT.add(poi[j].c,1); // cout<<l<<" "<<r<<" "<<i<<" "<<BIT.sum(poi[i].c)<<endl; poi[i].sum+=BIT.sum(poi[i].c); } for(int k=l;k<j;k++) BIT.add(poi[k].c,-1); } int main() { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) { scanf("%d%d%d",&poi[i].a,&poi[i].b,&poi[i].c); poi[i].sum=0; } sort(poi+1,poi+n+1,cmp); CDQ(1,n); sort(poi+1,poi+n+1,cmp2); for(int i=n-1;i>=1;i--) { if(poi[i+1].a==poi[i].a&&poi[i+1].b==poi[i].b&&poi[i+1].c==poi[i].c) poi[i].sum=max(poi[i].sum,poi[i+1].sum); } for(int i=2;i<=n;i++) { if(poi[i].a==poi[i-1].a&&poi[i].b==poi[i-1].b&&poi[i].c==poi[i-1].c) poi[i].sum=max(poi[i].sum,poi[i-1].sum); } for(int i=1;i<=n;i++) ans[poi[i].sum]++; for(int i=0;i<=n-1;i++) printf("%d\n",ans[i]); return 0; }