cdq分治模板

可求解多维偏序问题
三维偏序(陌上花开)
有 nn 个元素,第 ii 个元素有 \(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 的数量

输入格式
第一行两个整数 nn、kk,分别表示元素数量和最大属性值。
之后 nn 行,每行三个整数 \(a_i\)\(b_i\)\(c_i\) ,分别表示三个属性值。

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

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

输出

3
1
3
0
1
0
1
0
0
1

不同于普通分治的是:左右两个子区间并不是独立的,虽然不能使得两个子区间相互独立,但是通过某一维的排序,使得左子区间答案相对于右子区间独立,而右子区间最终计算时加上左子区间的贡献即可
因为可以等于,如果不去重的话子区间答案可能不独立。
如:a,b,c,d分为ab,cd,如果b和c相等,分治就不对了

#include<bits/stdc++.h>
using namespace std;
char buf[1<<20],*P1=buf,*P2=buf;
#define gc() (P1==P2&&(P2=(P1=buf)+fread(buf,1,1<<20,stdin),P1==P2)?EOF:*P1++)
#define TT template<class T>inline
TT bool read(T &x){
    x=0;char c=gc();bool f=0;
    while(c<48||c>57){if(c==EOF)return 0;f^=(c=='-'),c=gc();}
    while(47<c&&c<58)x=x*10+c-48,c=gc();
    if(f)x=-x;return 1;
}
TT bool read(T&a,T&b){return read(a)&&read(b);}
TT bool read(T&a,T&b,T&c){return read(a)&&read(b)&&read(c);}
typedef long long ll;
#define lowbit(x) (x&(-x))
const ll MAXN=1e5+8,mod=1e9+7,inf=0x3f3f3f3f;
struct Node{int a,b,c,num,ans;}node[MAXN],tmp[MAXN];
//num:去重后的和数量,该点的答案
bool cmp(const Node&x,const Node&y){
    if(x.a^y.a)return x.a<y.a;
    if(x.b^y.b)return x.b<y.b;
    return x.c<y.c;
}
bool equal(const Node&x,const Node&y){return x.a==y.a&&x.b==y.b&&x.c==y.c;}
int n,k,s[MAXN<<1];
inline void add(int p,int v){
    while(p<=k){
        s[p]+=v;
        p+=lowbit(p);
    }
}
inline int query(int p){
    int res=0;
    while(p){
        res+=s[p];
        p-=lowbit(p);
    }return res;
}
int d[MAXN];
void cdq(int l,int r){
    if(l==r)return;//如果只有一个点,ans=0;
    int m=(l+r)>>1;
    cdq(l,m);cdq(m+1,r);//递归求解子区间
    for(int i=l,j=m+1,p=l;i<=m||j<=r;++p){//更新答案的同时就可以归并排序
        if(i<=m&&(j>r||node[i].b<=node[j].b))
            add(node[i].c,node[i].num),tmp[p]=node[i++];
        else node[j].ans+=query(node[j].c),tmp[p]=node[j++];
    }
    for(int i=l;i<=m;++i)add(node[i].c,-node[i].num);//将树状数组清空
    for(int i=l;i<=r;++i)node[i]=tmp[i];
}
int main() {
    read(n,k);
    for(int i=1;i<=n;++i){
        read(node[i].a,node[i].b,node[i].c);
        node[i].num=1;
        node[i].ans=0;
    }
    sort(node,node+1+n,cmp);
    int cnt=1;//去重
    for(int i=2;i<=n;++i){
        if(equal(node[i],node[cnt]))node[cnt].num++;
        else node[++cnt]=node[i];
    }
    cdq(1,cnt);
    //这里的ans是只计算了不想等的答案,得加上重复的数量。
    for(int i=1;i<=cnt;++i)d[node[i].ans+node[i].num-1]+=node[i].num;
    for(int i=0;i<n;++i)printf("%d\n",d[i]);
    return 0;
}
posted @ 2020-12-19 22:54  肆之月  阅读(49)  评论(0编辑  收藏  举报