Note - CDQ分治/整體二分

子洢發表於2024-08-19

CDQ分治

基本思想是什麼?

面對一個離線的三維偏序問題,用 sort 最佳化掉一維,分治中最佳化掉另一維,資料結構最佳化一維。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,k,tot;
int s[N*2];
int ans[N];
struct nd{
    int a,b,c,cnt,sum;
    friend bool operator ==(nd x,nd y){
        return x.a==y.a&&x.b==y.b&&x.c==y.c;
    }
}t[N],q[N];
bool cmp(nd x,nd y){return x.a==y.a?(x.b==y.b?x.c<y.c:x.b<y.b):x.a<y.a;}
bool cmp2(nd x,nd y){return x.b==y.b?x.c<y.c:x.b<y.b;}
void update(int x,int v){for(;x<=2e5;x+=x&(-x))s[x]+=v;}
int ask(int x){int ret=0;for(;x;x-=x&(-x))ret+=s[x];return ret;}
void solve(int l,int r){
    if(l==r)return;
    int mid=l+r>>1;
    solve(l,mid);
    solve(mid+1,r);
    sort(q+l,q+mid+1,cmp2);
    sort(q+mid+1,q+r+1,cmp2);
    int i=mid+1,j=l;
    for(;i<=r;i++){
        while(j<=mid&&q[j].b<=q[i].b){
            update(q[j].c,q[j].cnt);
            ++j;
        }
        q[i].sum+=ask(q[i].c);
    }
    for(int k=l;k<j;k++)
        update(q[k].c,-q[k].cnt);
}
int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%d%d%d",&t[i].a,&t[i].b,&t[i].c);
    }
    sort(t+1,t+n+1,cmp);
    for(int i=1;i<=n;){
        int j=i+1;
        while(j<=n&&t[j]==t[i])++j;
        q[++tot]={t[i].a,t[i].b,t[i].c,j-i};
        i=j;
    }
    solve(1,tot);
    for(int i=1;i<=tot;i++)
        ans[q[i].sum+q[i].cnt-1]+=q[i].cnt;
    for(int i=0;i<n;i++)
        printf("%d\n",ans[i]);
    return 0;
}

整體二分

基本思想是什麼?

遞迴值域區間,更新左區間的位置,對於每個詢問,若 \(\sum^r_{i=l} cnt>=k\) 說明在左區間,否則在右區間。

有什麼獨特作用?

區間第 k 大可以用主席樹求,但矩形中的第 k 大隻能使用整體二分求。

例:P1527 [國家集訓隊] 矩陣乘法

void solve(int l,int r,int st,int ed){//l-r 為區間,st-ed 為詢問
    if(ed<st) return;
    if(l==r){
        for(int i=st;i<=ed;i++)
            ans[q[i].op]=l;
        return;
    }
    int mid=l+r>>1;
    int lt=0,rt=0;
    for(int i=st;i<=ed;i++){
        if(q[i].op>0){
            int t=ask(q[i].y)-ask(q[i].x-1);
            if(t>=q[i].v)lq[++lt]=q[i];
            else rq[++rt]=q[i],rq[rt].v-=t;
        }else{
            if(q[i].x<=mid)update(q[i].y,1),lq[++lt]=q[i];
            else rq[++rt]=q[i];
        }
    }
    for(int i=st;i<=ed;i++)
    	if(q[i].op==0&&q[i].x<=mid)update(q[i].y,-1);
    for(int i=1;i<=lt;i++)
        q[st+i-1]=lq[i];
    for(int i=1;i<=rt;i++)
        q[st+lt+i-1]=rq[i];
    solve(l,mid,st,st+lt-1);
    solve(mid+1,r,st+lt,ed);
}

相關文章