码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2018-12-06 22:21:48      阅读:280      评论:0      收藏:0      [点我收藏+]

标签:query   string   amp   统计   read   树状   []   答案   产生   

\(\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}{数据范围与提示}\)

\(1\leq n \leq 100000,1\leq k\leq 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('\n')
#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\n",ans[i]);
    return ~~(0^_^0);
}

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

标签:query   string   amp   统计   read   树状   []   答案   产生   

原文地址:https://www.cnblogs.com/olinr/p/10079577.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!