[BZOJ3262]陌上花开(cdq分治+bit)

来源:互联网 发布:社会化新媒体矩阵 编辑:程序博客网 时间:2024/06/05 02:57

题目:

陌上花开

题解:

第一篇cdq分治。以思考为主,是一道三维偏序的题目,还是很常见的方法

首先按照x第一关键字,y第二关键字,z第三关键字排序,然后问题就转化成了求某一个(x,y,z)前面存在多少个(x’,y’,z)满足x>=x’,y>=y’,z>=z’ 由于x一定是有序的,那么根据x进行cdq分治 每一次将(l,mid)和(mid+1,r)分别根据y排序(x不一定有序),然后两个指针扫一下,查询之前把z小于等于它的都插入到bit中 

这样做完了之后还有一个问题是有一些(x,y,z)是完全相同的,这个时候只有排在最后一个的统计的是全部的,所以从后往前max一遍最后统计答案就可以了

代码:

#include <cstdio>#include <algorithm>using namespace std;struct hh{int x,y,z,id,ans;}f[100005],a[100005],b[100005];int ch[200005],c[200005],k,n,ac,bc,pa,pb,tot,cnt[200005];int cmp1(hh a,hh b){return (a.x<b.x)||(a.x==b.x&&a.y<b.y)||(a.x==b.x && a.y==b.y && a.z<b.z);}int cmp2(hh a,hh b){return a.y<b.y;}void add(int loc,int v){for (int i=loc;i<=k;i+=i&(-i))  c[i]+=v;}int qurry(int loc){int ans=0;for (int i=loc;i>=1;i-=i&(-i))  ans+=c[i];return ans;}void cdq(int l,int r)//进去的时候已经按照a排好序,出来的时候已经按照b排好序,反正右边的a肯定比左边的大啊,然后在c的位置+1,统计b[i]之前的和就是比c大的个数 {if (l>=r) return;int mid=(l+r)>>1;cdq(l,mid);ac=0;for (int i=l;i<=mid;i++) a[++ac]=f[i];sort(a+1,a+ac+1,cmp2);bc=0;for (int i=mid+1;i<=r;i++) b[++bc]=f[i];sort(b+1,b+bc+1,cmp2);pa=pb=1; tot=0;while (pb<=bc){while (pa<=ac && a[pa].y<=b[pb].y){add(a[pa].z,1);ch[++tot]=a[pa].z;++pa;}f[b[pb].id].ans+=qurry(b[pb].z);++pb;}for (int i=1;i<=tot;i++)  add(ch[i],-1);cdq(mid+1,r);}int main(){int i;scanf("%d%d",&n,&k);for (i=1;i<=n;i++)  scanf("%d%d%d",&f[i].x,&f[i].y,&f[i].z);sort(f+1,f+n+1,cmp1);    for (int i=1;i<=n;i++) f[i].id=i;cdq(1,n);for (int i=n-1;i>=1;i--)  if (f[i].x==f[i+1].x && f[i].y==f[i+1].y && f[i].z==f[i+1].z)    f[i].ans=max(f[i].ans,f[i+1].ans);for (int i=1;i<=n;i++)  ++cnt[f[i].ans];for (int i=0;i<n;i++)  printf("%d\n",cnt[i]);}


原创粉丝点击