zoukankan      html  css  js  c++  java
  • Luogu P3810 【模板】三维偏序(陌上花开)(CDQ分治)

    题目
    以三维偏序为例来讲一下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('
    ');}
    }
    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;
    }
    
  • 相关阅读:
    Java 的Throwable、error、exception的区别
    最长回文子序列和最长回文子串
    牛客练习赛40 C-小A与欧拉路
    判断一棵树是否为二叉搜索树,完全二叉树和二叉平衡树
    Java语言的特点和特性
    设计模式
    联合索引和单列索引
    如何优化sql查询
    数据库的范式和约束
    数据库事务ACID和事务的隔离级别
  • 原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/11938390.html
Copyright © 2011-2022 走看看