cdq分治学习笔记
cdq分治学习笔记
说是学习笔记,但是迄今为止也只A了模板,主要是没找到cdq的题目,以后遇到了慢慢补吧。
cdq看起来难,说来也简单。
将区间内的符合某种条件的数,分为经过中点(从左区间到右区间)的数,再分割为两个更小的区间。
给个图吧:
一般用作处理区间内满足条件的三元对,二元对问题。
一般处理区间时会排序,故时间复杂度为\(O(\sum_{i=1}^{m} {len_{i}}*log_{2} len_{i})<=O(\sum_{i=1}^{m} len_{i}*log_{2} n)=O(n {log_{2} n}^{2})\)
模板:三维偏序
第一维排序,第二维cdq,第三维树状数组。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+7,M=2e5+7;
int n,m=0,k,t,v[N],x[M];
struct xd{int a,b,c,d,e;}p[N],q[N];
inline int read(){
int T=0,F=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') F=-1; ch=getchar();}
while(ch>='0'&&ch<='9') T=(T<<3)+(T<<1)+(ch-48),ch=getchar();
return F*T;
}
bool cmp(xd u,xd v){return u.a!=v.a?u.a<v.a:(u.b!=v.b?u.b<v.b:u.c<v.c);}
bool cmp2(xd u,xd v){return u.b!=v.b?u.b<v.b:u.c<v.c;}
inline int lowbit(int u){return u&(-u);}
void add(int u,int v){for(int i=u;i<=k;i+=lowbit(i)) x[i]+=v;}
int getsum(int u){
int ans=0;
for(int i=u;i;i-=lowbit(i)) ans+=x[i];
return ans;
}
void cdq(int l,int r){
if(l==r) return;
int mid=l+r>>1;
cdq(l,mid),cdq(mid+1,r);
sort(q+l,q+mid+1,cmp2),sort(q+mid+1,q+r+1,cmp2);
t=l;
for(int i=mid+1;i<=r;++i){
while(t<=mid&&q[i].b>=q[t].b) add(q[t].c,q[t].d),++t;
q[i].e+=getsum(q[i].c);
}
for(int i=l;i<t;++i) add(q[i].c,-q[i].d);
}
int main(){
n=read(),k=read();
for(int i=1;i<=n;++i) p[i].a=read(),p[i].b=read(),p[i].c=read();
sort(p+1,p+n+1,cmp);
for(int i=1;i<=n;++i){
if(p[i].a!=p[i-1].a||p[i].b!=p[i-1].b||p[i].c!=p[i-1].c) ++m,q[m]=p[i],q[m].d=1;
else ++q[m].d;
}
cdq(1,m);
for(int i=1;i<=m;++i) v[q[i].e+q[i].d-1]+=q[i].d;
for(int i=0;i<n;++i) printf("%d\n",v[i]);
return 0;
}