CDQ分治

Posted on 2026-08-24 20:48  暮光落阳  阅读(3)  评论(0)    收藏  举报

CDQ分治

所以,什么是CDQ分治?


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

三维偏序,即求同时满足 \(a_i<a_j,b_i<b_j,c_i<c_j\)\((i,j)\) 点对数。

二维偏序,类似逆序对,用树状数组或归并排序均可解决。\(O(n\log n)\)

树状数组 用权值树状数组维护当前位置左侧 大于/小于 某数的数量
归并排序 在合并过程中统计右侧区间先于左侧区间加入的元素数量
对于三维偏序,为了同时维护三个维度的性质,需要同时使用树状数组和归并排序。
  1. 按照第一维排序。

  2. 按照第二维进行归并排序。在合并过程中,左侧区间的 \(a\) 值必然小于右侧区间,那么,当且仅当右侧区间的元素 \(j\)\(b\) 值大于左侧区间元素 \(i\) 时,前两维条件满足。

  3. 由于两区间均已依照 \(b\) 排序,所以可通过建立以 \(c\) 为下标的树状数组,在合并过程中统计符合第三维条件的点对数量。(若左侧指向元素 \(b\) 值小于等于右侧指向元素,则使树状数组中左侧指向元素 \(c\) 值位置 \(+1\) ,若左侧指向元素 \(b\) 值大于右侧指向元素,则查询树状数组中小于右侧指向元素 \(c\) 值的数量,累加进右侧指向元素的答案中)

时间复杂度 \(O(n\log ^2n)\)

Code
#include <bits/stdc++.h>
#define int long long
using namespace std;

int read(){
    int i;
    scanf("%lld",&i);
    return i;
}

int n,k;

class CIS{
    array<int,200010> tr={};
    public:
    void add(int p,int v){
        while(p<=k){
            tr[p]+=v;
            p+=(p&(-p));
        }
        //for(int i=1;i<=k;i++)printf("%lld%c",tr[i]," \n"[i==k]);
    }
    int ask(int r){
        int ans=0;
        while(r){
            ans+=tr[r];
            r-=(r&(-r));
        }
        return ans;
    }
};

struct Fl{
    int a,b,c;
    bool operator <(const Fl &f)const{
        if(a!=f.a)return a<f.a;
        else if(b!=f.b)return b<f.b;
        else return c<f.c;
    }
    bool operator ==(const Fl &f)const{
        return a==f.a&&b==f.b&&c==f.c;
    }
};

array<Fl,100010> arr;
map<Fl,int> ma;
CIS cs;
int ans[100010]={};

queue<int> remerge(int st,int en){
    queue<int> un;
    if(st==en){
        un.push(st);
        return un;
    }
    int mid=(st+en)/2;
    queue<int> p1=remerge(st,mid),p2=remerge(mid+1,en);
    queue<int> pp;
    while((!p1.empty())&&(!p2.empty())){
        if(arr[p1.front()].b>arr[p2.front()].b){
            un.push(p2.front()),ans[p2.front()]+=cs.ask(arr[p2.front()].c),p2.pop();
        }else{
            un.push(p1.front()),cs.add(arr[p1.front()].c,ma[arr[p1.front()]]),pp.push(p1.front()),p1.pop();
        }
    }
	while(!p1.empty())un.push(p1.front()),p1.pop();
    while(!p2.empty())un.push(p2.front()),ans[p2.front()]+=cs.ask(arr[p2.front()].c),p2.pop();
    while(pp.size())cs.add(arr[pp.front()].c,-ma[arr[pp.front()]]),pp.pop();
    return un;
}

int init(){
    n=read(),k=read();
    for(int i=1;i<=n;i++)arr[i].a=read(),arr[i].b=read(),arr[i].c=read(),ma[arr[i]]++;
    sort(arr.begin()+1,arr.begin()+n+1);
    unique(arr.begin()+1,arr.begin()+n+1);
    int li=ma.size();
    queue<int> qu=remerge(1,li);
    int num[100010]={};
    int cnt=1;
    for(map<Fl,int>::iterator it=ma.begin();it!=ma.end();it++,cnt++)num[ans[cnt]+it->second-1]+=it->second;
    for(int i=0;i<n;i++)printf("%lld\n",num[i]);
    return 3;
}

int three_dimensional_partial_order=init();

signed main(){
    return 0;
}