题解 P3810 【【模板】三维偏序(陌上花开)】

· · 题解

戳我食用效果更加

前言

陌上花开,可缓缓归矣                         ——吴越王

  1. 寓意:意思是:田间阡陌上的花开了,你可以一边赏花,一边慢慢回来。
  2. 隐意:春天都到了,你怎么还没有回来。形容吴越王期盼夫人早日归来的急切心情。

Ask:那么这和cdq有什么关系呢?
Answer:并没有什么关系,增强语文水平而已,现在来看一到题目:陌上花开。这就有关系了吧。

题目大意是:有n个元素,第i个元素有a_i,b_i,c_i三个属性,设f(i)表示满足a_j≤a_ib_j≤b_ic_j≤c_ij的数量。求f(i)=d的数量d\in[0,n)

做法1:暴力

```cpp #include<bits/stdc++.h> int k,n,f[200001],a[200001],b[200001],c[200001],ans; int main(){ scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) scanf("%d%d%d",&a[i],&b[i],&c[i]); for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++) if(a[j]<=a[i]&&b[j]<=b[i]&&c[j]<=c[i]&&i!=j) ans++; f[ans]++,ans=0; } for(int i=0;i<n;i++) printf("%d\n",f[i]); } ``` 这个应该不需要多讲吧,普及组的难度,但不要说不需要,你在对拍的时候就需要他了 做法2: K-DTree ~~不会,我tcl~~ 做法三:cdq分治 现在来正式讲一讲cdq分治 ### cdq分治 ####前置要求: 1. [树状数组](https://www.cnblogs.com/hbxblog/p/9866972.html) 2. [基础分治](http://baidu.physton.com/?q=去普及组吧,这不是你改来的地方) 3. [树状数组求逆序对](https://www.cnblogs.com/hbxblog/p/10138151.html) 逆序对的问题是二维的,我们只需要讲一维排序,然后在用树状数组维护即可。 那么对于三维的陌上花开呢?我们还是可以用这个方法,首先先将数列按第一位排序,这样我们只需要考虑两维的情况。于是我们可以分治做了,将某一个序列$[l,r]$,分成段$[l,mid]$和$[mid+1,r]$,然后在对$[l,r]$这段区间的第二维进行排序。若点在排序前属于$[l,mid]$,树状数组单点修改;否则该点在排序前属于$[m+1,r]$,便统计一次。(其实就是类似于树状数组求逆序对的操作) **一定要记得去重,否则会出事的** #### code ``` cpp #include<bits/stdc++.h> using namespace std; const int N=200001; struct node{ int x,y,z,id; }a[N]; int c[N<<2],k,n,b[N],bj[N],f[N]; int lowbit(int x){ return x&(-x); } int read(){ int x=0,f=1; char c=getchar(); while(c<'0'||c>'9') f=(c=='-')?-1:1,c=getchar(); while(c>='0'&&c<='9') x=x*10+c-48,c=getchar(); return f*x; } void add(int x,int v){ while(x<=k) c[x]+=v,x+=lowbit(x); } int sum(int x){ int ans=0; while(x) ans+=c[x],x-=lowbit(x); return ans; } bool cmp1(const node & a , const node & b ){ if(a.x!=b.x) return a.x<b.x; if(a.y!=b.y) return a.y<b.y; return a.z<b.z; } bool cmp2(const node & a , const node & b ){ if(a.y!=b.y) return a.y<b.y; if(a.z!=b.z) return a.z<b.z; return a.x<b.x; } void cdq(int l,int r){ if(l==r) return ; int mid=(l+r)>>1,flag; cdq(l,mid),cdq(mid+1,r); sort(a+l,a+r+1,cmp2); for(int i=l;i<=r;i++) (a[i].x<=mid)?add(a[i].z,1),flag=i:b[a[i].id]+=sum(a[i].z); for(int i=l;i<=r;i++) if(a[i].x<=mid) add(a[i].z,-1); } int main(){ n=read(),k=read(); for(int i=1;i<=n;i++) a[i].x=read(),a[i].y=read(),a[i].z=read(),a[i].id=i; sort(a+1,a+1+n,cmp1); for(int i=1;i<=n;){ int j=i+1; while(j<=n&&a[j].x==a[i].x&&a[j].y==a[i].y&&a[j].z==a[i].z) j++; while(i<j) bj[a[i].id]=a[j-1].id,i++; } for(int i=1;i<=n;i++) a[i].x=i; cdq(1,n); for(int i=1;i<=n;i++) f[b[bj[a[i].id]]]++; for(int i=0;i<n;i++) printf("%d\n",f[i]); } ```