CDQ分治
CDQ分治是一种分治类算法,可以解决三维以及多维偏序 ( CDQ套CDQ套CDQ套CDQ套CDQ套CDQ套CDQ套CDQ套CDQ套CDQ套CDQ套CDQ套CDQ套CDQ套CDQ套CDQ套CDQ套CDQ套CDQ套CDQ套CDQ套CDQ套CDQ套CDQ套CDQ套CDQ套CDQ套CDQ套CDQ套CDQ ) 问题。
其思想类似于归并排序,在子问题对当前问题有贡献时可以使用QAQ 例如板题陌上花开(三位偏序) (我不会CDQ套CDQ)首先我们按照(a)升序排序 显然接下来我们需要解决的问题就是(b,c) 的偏序问题了 因为我们已经按照a升序排序了 所以在分治后的左区间在满足(b_l<b_r)&&(c_l<c_r)的 时候会对右区间产生贡献 我们可以分别对左区间,右区间按照(b)排序,然后用树状数组维护(c)就可以了
注意要去重,因为如果相同的数却被分在了左右区间 会产生意想不到的结果哦QAQ
另外如果将区间按照(b)排序了 那么如果(b_r<b_l) 就可以直接更新(b_r)对应的(data)的(ans)了。因为显然(b_{l+1}>b_r) (毕竟按照b排序了)
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
using namespace std;
const int maxn=100100;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct node{
int a,b,c,cnt,ans;
#define a(x) num[x].a
#define b(x) num[x].b
#define c(x) num[x].c
}num[maxn],data[maxn];
inline bool cmpl(node x,node y){return x.a<y.a || (x.a==y.a && (x.b<y.b || (x.b==y.b && x.c<y.c)));}
inline bool cmpll(node x,node y){return x.b<y.b || (x.b==y.b && x.c<y.c);}
int nn,n,k,tot,tree[maxn<<1],shawn[maxn<<1];
void add(int x,int y){
for(;x<=k;x+=(x&-x)){
tree[x]+=y;
}
}
int query(int x){
int sum=0;
for(;x;x-=(x&-x)){
sum+=tree[x];
}
return sum;
}
void cdq(int l,int r){
if(l==r) return ;
int mid=l+r>>1;
cdq(l,mid);cdq(mid+1,r);
sort(data+l,data+mid+1,cmpll);sort(data+mid+1,data+r+1,cmpll);
int i=mid+1,j=l;
for(;i<=r;i++){
while(data[j].b<=data[i].b && j<=mid){
add(data[j].c,data[j].cnt);j++;
}
data[i].ans+=query(data[i].c);
}
for(i=l;i<j;i++)
add(data[i].c,-data[i].cnt);
}
int main(){
nn=read();k=read();
for(int i=1;i<=nn;i++){
a(i)=read();b(i)=read();c(i)=read();
}
sort(num+1,num+1+nn,cmpl);
for(int i=1;i<=nn;i++){
tot++;
if(a(i)!=a(i+1) || b(i)!=b(i+1) || c(i)!=c(i+1)){data[++n]=num[i];data[n].cnt=tot;tot=0;}
}
cdq(1,n);
for(int i=1;i<=n;i++){
shawn[data[i].ans+data[i].cnt-1]+=data[i].cnt;
}
for(int i=0;i<nn;i++)
printf("%d
",shawn[i]);
return 0;
}