首页 > 其他 > 详细

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

时间:2018-09-17 14:30:00      阅读:175      评论:0      收藏:0      [点我收藏+]

传送门

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\n",ans[i]);
    return 0;
}

 

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

原文:https://www.cnblogs.com/LLTYYC/p/9661831.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!