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);
}