Luogu3810 三维偏序(陌上花开)

原题链接:https://www.luogu.org/problemnew/show/P3810

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

题目背景

这是一道模板题

可以使用bitset,CDQ分治,K-DTree等方式解决。

题目描述

n n 个元素,第i 个元素有 ai a i bi b i ci c i 三个属性,设 f(i) f ( i ) 表示满足 ajai a j ≤ a i bjbi b j ≤ b i cjci c j ≤ c i j j 的数量。

对于 d[0,n),求 f(i)=d f ( i ) = d 的数量

输入输出格式
输入格式:

第一行两个整数 n n k k ,分别表示元素数量和最大属性值。

之后n行,每行三个整数 ai a i bi b i ci c i ,分别表示三个属性值。

输出格式:

输出 n n 行,第d+1行表示 f(i)=d f ( i ) = d i i 的数量。

输入输出样例
输入样例#1:

10 3
3 3 3
2 3 3
2 3 1
3 1 1
3 1 2
1 3 1
1 1 2
1 2 2
1 3 2
1 2 1

输出样例#1:

3
1
3
0
1
0
1
0
0
1

说明

1n100000,1k200000

题解

考虑使用 cdq c d q 分治,第一维直接排序搞掉,第二维 cdq c d q 搞成离线,最后一位上数据结构即可。

左半边对右半边的贡献很好统计,左边为单点加,右边为区间求和。

代码
#include<bits/stdc++.h>
using namespace std;
const int M=1e6+5;
struct sd{int p,q,r,sum,cot,id;};
bool cmp1(sd a,sd b)
{
    if(a.p!=b.p)return a.p<b.p;
    if(a.q!=b.q)return a.q<b.q;
    return a.r<b.r;
}
bool cmp2(sd a,sd b){return a.q==b.q?(a.r==b.r?a.p<b.p:a.r<b.r):a.q<b.q;}
bool operator !=(sd a,sd b){return a.p!=b.p||a.q!=b.q||a.r!=b.r;}
int sum[M],ans[M],n,tot,base=1;
sd x[M],que[M];
void in()
{
    int a;
    scanf("%d%d",&n,&a);
    while(base<a)base<<=1;
    for(int i=1;i<=n;++i)scanf("%d%d%d",&x[i].p,&x[i].q,&x[i].r);
}
void add(int v,int s){v+=base;for(;v;v>>=1)sum[v]+=s;}
int query(int le,int ri)
{
    int ans=0;le+=base;ri+=base;
    for(;le^ri^1;le>>=1,ri>>=1)
    {
        if(le&1^1)ans+=sum[le+1];
        if(ri&1)ans+=sum[ri-1];
    }
    return ans;
}
void cdq(int le,int ri)
{
    if(le==ri)return;
    int mid=(le+ri)>>1;
    cdq(le,mid);cdq(mid+1,ri);
    sort(que+le,que+ri+1,cmp2);
    for(int i=le;i<=ri;++i)
    {
        if(que[i].id<=mid)add(que[i].r,que[i].cot);
        else que[i].sum+=query(0,que[i].r+1);
    }
    for(int i=le;i<=ri;++i)
    if(que[i].id<=mid)add(que[i].r,-que[i].cot);
}
void ac()
{
    sort(x+1,x+1+n,cmp1);
    for(int i=1;i<=n;++i)
    {
        if(x[i]!=x[i-1])que[++tot]=x[i],que[tot].id=tot;
        ++que[tot].cot;
    }
    cdq(1,tot);
    for(int i=1;i<=tot;++i)ans[que[i].sum+que[i].cot-1]+=que[i].cot;
    for(int i=0;i<n;++i)printf("%d\n",ans[i]);
}
int main()
{
    in();ac();
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值