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

    传送门

    CDQ分治

    先三关键字排序

    然后把第二关键字归并排序

    在合并子区间时用 第三关键字的权值树状树组 算出子区间的答案

    为什么可以这样搞呢

    首先第一维已经有序

    所以只要考虑左边对右边的影响

    把第二维归并时 左子区间的第一二关键字 全部小于或等于 右子区间的第一二关键字

    所以也只要考虑左边对右边的影响

    就用 第三关键字权值树状树组 搞一搞 二维偏序 就好了

    二维偏序应该懂吧,实现和细节还是看代码吧

    还要注意有重复的数

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    inline int read()
    {
        int x=0; char ch=getchar();
        while(ch<'0'||ch>'9') ch=getchar();
        while(ch>='0'&&ch<='9')
        {
            x=(x<<1)+(x<<3)+(ch^48);
            ch=getchar();
        }
        return x;
    }
    
    const int N=4e5+7;
    int n,m;
    int a[N],b[N],c[N],p[N],cnt[N],ans[N],v[N];
    //a,b,c为三个关键字,p存排序去重后的数据,这里没用结构体,可以省空间
    //v是每个数重复的数量
    inline bool cmp1(const int &x,const int &y){ return a[x]<a[y]|| (a[x]==a[y]&& (b[x]<b[y]|| (b[x]==b[y]&&c[x]<c[y]) ) ); }
    //不用结构体排序的骚操作
    
    int t[N];//数状数组
    inline void add(int x,int v)
    {
        while(x<=m)
        {
            t[x]+=v;
            x+=x&-x;
        }
    }
    inline int query(int x)
    {
        int res=0;
        while(x)
        {
            res+=t[x];
            x-=x&-x;
        }
        return res;
    }
    
    int tmp[N];//归并时的临时数组
    void cdq(int l,int r)
    {
        if(l==r) return;
        int mid=(l+r)>>1;
        cdq(l,mid); cdq(mid+1,r);//先归并处理子区间
    
        int i=l,j=mid+1,tot=0;
        while(i<=mid&&j<=r)
        {
            int x=p[i],y=p[j];
            if(b[x]<=b[y]) add(c[x],v[x]),tmp[++tot]=x,i++;
            else cnt[y]+=query(c[y]),tmp[++tot]=y,j++;
        }//核心,树状数组维护二维偏序
    
        while(j<=r) cnt[p[j]]+=query(c[p[j]]),tmp[++tot]=p[j],j++;
        while(i<=mid) tmp[++tot]=p[i],i++;
        //可能还有一些没处理完
    
        int mx=b[p[r]];
        for(i=l; i<=mid&&b[p[i]]<=mx ;i++) add(c[p[i]],-v[p[i]]);//用完要这样清空
        for(i=l;i<=r;i++) p[i]=tmp[i-l+1];//归并排序
    }
    
    int main()
    {
        n=read(); m=read();
        for(int i=1;i<=n;i++)
            p[i]=i,a[i]=read(),b[i]=read(),c[i]=read();
    
        sort(p+1,p+n+1,cmp1);
        int tot=1;
        for(int i=2;i<=n;i++)
        {
            int x=p[i],y=p[tot];
            v[y]++;
            if(a[x]^a[y]||b[x]^b[y]||c[x]^c[y]) p[++tot]=x;
        }
        v[p[tot]]++;
        //去重并计算重复的数量
    
        cdq(1,tot);
        for(int i=1;i<=tot;i++) ans[cnt[p[i]]+v[p[i]]-1]+=v[p[i]];//注意有重复的数字
        for(int i=0;i<n;i++) printf("%d
    ",ans[i]);
        return 0;
    }
  • 相关阅读:
    chpa02_用分治法构造二叉树
    通过Wubi来安装Ubuntu 10.04详细(图文版)
    NHibernate 数据库支持分类配置
    flash或是Flex客户端调用服务器端的方法
    Windows 7使用wubi硬盘安装Ubuntu (文字版)
    使用PropertyInfo类反射获取类 的类型
    转换json格式的日期
    原创新闻 并非愚人节玩笑,Oracle挥舞法律大棒,JavaEye被逼改名ItEye
    DataTable或DataSet转JSON
    NHibernate + AspNetPager 实现分页
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/9661831.html
Copyright © 2011-2022 走看看