ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2019-11-26 21:03:57  阅读:338  来源: 互联网

标签:偏序 右边 维都 Luogu 分治 归并 P3810 左边 升序


题目
以三维偏序为例来讲一下CDQ分治。
CDQ的本质就是把一个序列分成两段,计算左边对右边的贡献,然后分治。
不过一般都是先分治到底再从下往上算,这样可以先归并再算。
比如这道题,我们先按第一维排序,然后分治完下一层之后边归并排序边算贡献。
具体大概是这样:
比如我们已经把下面的全部算完了,那么传上来的左边和有边一定满足:
1、左边的第一维都比右边的第一维小。
2、左边和右边在第二维上都是升序的。
那么我们归并排序,遇到左边的就加入数据结构修改贡献,遇到右边的就计算贡献。
同时我们能够保证传上去的是在第二维上是升序的。
在这一题的话,因为我们满足左边的第一维都比右边的小,两边的第二维都是升序,所以我们在归并的过程中只要对每个右边的计算前面的左边的第三维比它小的个数。这个可以通过离散化+去重+BIT维护桶解决。
跟整体二分一样,最后的清零要用撤销而不是memset,否则复杂度bomb。

#include<bits/stdc++.h>
using namespace std;
namespace IO
{
    char ibuf[(1<<21)+1],obuf[(1<<21)+1],st[15],*iS,*iT,*oS=obuf,*oT=obuf+(1<<21);
    char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
    void Flush(){fwrite(obuf,1,oS-obuf,stdout),oS=obuf;}
    void Put(char x){*oS++=x;if(oS==oT)Flush();}
    int read(){int x=0,c=Get();while(!isdigit(c))c=Get();while(isdigit(c))x=x*10+c-48,c=Get();return x;}
    void write(int x){int top=0;if(!x)Put('0');while(x)st[++top]=(x%10)+48,x/=10;while(top)Put(st[top--]);Put('\n');}
}
using namespace IO;
const int N=100007,M=N<<1;
int k,p[N],q[N],a[N],b[N],c[N],t[M],v[N],cnt[N],ans[N];
int cmp(int x,int y){return a[x]<a[y]||(a[x]==a[y]&&(b[x]<b[y]||(b[x]==b[y]&&c[x]<c[y])));}
void modify(int i,int v){for(;i<=k;i+=i&-i)t[i]+=v;}
int query(int i){int sum=0;for(;i;i-=i&-i)sum+=t[i];return sum;}
void cdq(int *p,int n)
{
    if(n==1) return;
    int m=n>>1,i,j,k,x,y;
    for(cdq(p,m),cdq(p+m,n-m),memcpy(q,p,n<<2),k=i=0,j=m;i<m&&j<n;++k)
    {
    x=q[i],y=q[j];
    if(b[x]<=b[y]) modify(c[p[k]=x],v[x]),++i; else cnt[y]+=query(c[p[k]=y]),++j;
    }
    for(;j<n;++j) cnt[q[j]]+=query(c[q[j]]);
    for(memcpy(p+k,q+i,(m-i)<<2),--i;~i;--i) modify(c[q[i]],-v[q[i]]);
}
int main()
{
    int n=read(),i,j,x,y;
    for(k=read(),i=0;i<n;++i) p[i]=i,a[i]=read(),b[i]=read(),c[i]=read();
    for(sort(p,p+n,cmp),i=1,j=0;i<n;++i)
    {
    x=p[i],y=p[j],++v[y];
    if(a[x]^a[y]||b[x]^b[y]||c[x]^c[y]) p[++j]=x;
    }
    ++v[p[j++]],cdq(p,j);
    for(i=0;i<j;++i) ans[cnt[p[i]]+v[p[i]]-1]+=v[p[i]];
    for(i=0;i<n;++i) write(ans[i]);
    return Flush(),0;
}

标签:偏序,右边,维都,Luogu,分治,归并,P3810,左边,升序
来源: https://www.cnblogs.com/cjoierShiina-Mashiro/p/11938390.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有