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

Posted olinr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P3810 模板三维偏序(陌上花开)相关的知识,希望对你有一定的参考价值。

(color{#0066ff}{题目描述})

有 n 个元素,第 i 个元素有 (a_i 、b_i 、c_i) 三个属性,设 f(i) 表示满足 (a_j leq a_i 且 b_j leq b_i 且 c_j leq c_i) 的 j 的数量。

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

(color{#0066ff}{输入格式})

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

之后 n 行,每行三个整数 (a_i 、b_i 、c_i) ,分别表示三个属性值。

(color{#0066ff}{输出格式})

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

(color{#0066ff}{输入样例})

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

(color{#0066ff}{输出样例})

3
1
3
0
1
0
1
0
0
1

(color{#0066ff}{数据范围与提示})

(1leq n leq 100000,1leq kleq 200000)

(color{#0066ff}{题解})

cdq 分治

对序列分治,每次统计左区间每个元素对于右区间每个元素的贡献

首先分别以x,y,z为1,2,3关键字排序

那么现在x是有序的

然后对序列cdq分治,每次把左右区间按y,z排序

这样保证了左区间所有x(leq)右区间所有x,且左右区间y有序

维护左区间的指针,for右区间每一个元素,只要左区间指针的元素y比当前小,就在权值树状数组上以z为下标++

然后用当前的z收集ans

注意千万不要单开ans数组收集答案

因为当前元素在每次sort后会变成其他的,这样收集就乱了

最后开桶统计即可

#include<cstdio>
#include<queue>
#include<vector>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cctype>
#include<cmath>
#define _ 0
#define LL long long
#define Space putchar(' ')
#define Enter putchar('
')
#define fuu(x,y,z) for(int x=(y),x##end=z;x<=x##end;x++)
#define fu(x,y,z)  for(int x=(y),x##end=z;x<x##end;x++)
#define fdd(x,y,z) for(int x=(y),x##end=z;x>=x##end;x--)
#define fd(x,y,z)  for(int x=(y),x##end=z;x>x##end;x--)
#define mem(x,y)   memset(x,y,sizeof(x))
#ifndef olinr
inline char getc()
{
    static char buf[100001],*p1=buf,*p2=buf;
    return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100001,stdin),p1==p2)? EOF:*p1++;
}
#else
#define getc() getchar()
#endif
template<typename T>inline void in(T &x)
{
    int f=1; char ch; x=0;
    while(!isdigit(ch=getc()))(ch=='-')&&(f=-f);
    while(isdigit(ch)) x=x*10+(ch^48),ch=getc();
    x*=f;
}
int n,k;
struct BIT
{
    private:
        int c[205050];
        #define low(x) ((x)&(-x))
    public:
        inline void add(int pos,int v) {for(int i=pos;i<=k;i+=low(i)) c[i]+=v;}
        inline int query(int pos) {int ans=0; for(int i=pos;i;i-=low(i)) ans+=c[i]; return ans;} 
};
struct node
{
    int x,y,z;
    int num,ans;
    friend bool operator != (const node &a,const node &b)
    {
        return a.x!=b.x||a.y!=b.y||a.z!=b.z;
    }
}e[300000];
BIT t;
int ans[300000];
inline void cdq(int l,int r)
{
    if(l==r) return ;
    int mid=(l+r)>>1;
    cdq(l,mid);cdq(mid+1,r);
    std::sort(e+l,e+mid+1,[](const node &a,const node &b){return (a.y<b.y)||(a.y==b.y&&a.z<b.z);});
    std::sort(e+mid+1,e+r+1,[](const node &a,const node &b){return (a.y<b.y)||(a.y==b.y&&a.z<b.z);});
    int now=l;
    fuu(i,mid+1,r)
    {
        //每一个都会产生贡献,所以是num
        while(now<=mid&&e[now].y<=e[i].y) t.add(e[now].z,e[now].num),now++;
        e[i].ans+=t.query(e[i].z);
    }
    fuu(i,l,now-1) t.add(e[i].z,-e[i].num);
}
int main()
{
    in(n),in(k);
    fuu(i,1,n) in(e[i].x),in(e[i].y),in(e[i].z),e[i].num=1;
    std::sort(e+1,e+n+1,[](const node &a,const node &b){return (a.x<b.x)||(a.x==b.x&&(a.y<b.y))||(a.x==b.x&&a.y==b.y&&a.z<b.z);});
    int tmp=0;
    //去重,相同的压一起(跟自己相同的算在ans的一部分,但自己对自己无贡献)
    fuu(i,1,n)
    {  
        if(e[i]!=e[i-1]) e[++tmp]=e[i];
        else e[tmp].num++;
    } 
    cdq(1,tmp);
    //ans里面套的是当前元素的答案(要加上相同的-自己)
    //外面因为跟自己相同的都是成立的,所以+=个数
    fuu(i,1,tmp) ans[e[i].ans+e[i].num-1]+=e[i].num;
    fuu(i,0,n-1) printf("%d
",ans[i]);
    return ~~(0^_^0);
}

以上是关于P3810 模板三维偏序(陌上花开)的主要内容,如果未能解决你的问题,请参考以下文章

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

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

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

P3810 模板三维偏序(陌上花开)cdq分治

洛谷 P3810 模板三维偏序(陌上花开) (cdq分治模板)

P3810 -三维偏序(陌上花开)cdq-分治