cdq 分治
事实上是一种比较简单的思想。
一般解决数对计数之类的问题。有的时候也用来优化 DP 的转移。
基本思想是考虑将数对分成左右端点全在 \([l,mid]\) 中,左右端点全在 \([mid+1,r]\) 中,左端点在 \([l,mid]\) 右端点在 \([mid+1,r]\) 中三类。然后前两类递归处理,考虑快速处理第三类。
对于第三类点,如果我们要花 \(f(r-l+1)\) 的时间复杂度解决第三类点,那么总复杂度为 \(O(f(n)\log n)\)。证明考虑类似线段树的结构。
我们以模板题为例。
P3810 【模板】三维偏序(陌上花开)
先将完全相同的点合并(因为内部的贡献不好在序列上表示),然后按 \(a\) 排序,然后做上述的过程。
具体的,我们现在要解决 \([l,r]\) 内,左端点在 \([l,mid]\),右端点在 \([mid+1,r]\) 中的合法数对。
由于已经对 \(a_i\) 排序,因此右半区间中的所有 \(a\) 一定都大于左半区间的 \(a\)。因此现在问题就变成了一个二位偏序问题。
于是我们对 \([l,mid],[mid+1,r]\) 两个区间分别以 \(b\) 为第一关键字排序。相当于用双指针扫 \(b\) 这一维,然后用树状数组统计 \(c\) 的信息即可。
注意在 cdq 内部排序是无影响的,我们只需要保证 \([l,r]\) 内的点集不变即可。注意由于对两边分别以 \(b\) 为第一关键字排序会破坏两边 \(a\) 的顺序,因此要先递归两边。
code
点击查看代码
#include<bits/stdc++.h>
bool Mbe;
using namespace std;
#define ll long long
//namespace FIO{
// template<typename P>
// inline void read(P &x){P res=0,f=1;char ch=getchar();while(ch<'0' || ch>'9'){if(ch=='-') f=-1;ch=getchar();}while(ch>='0' && ch<='9'){res=(res<<3)+(res<<1)+(ch^48);ch=getchar();}x=res*f;}
// template<typename Ty,typename ...Args>
// inline void read(Ty &x,Args &...args) {read(x);read(args...);}
// inline void write(ll x) {if(x<0ll)putchar('-'),x=-x;static int sta[35];int top = 0;do {sta[top++] = x % 10ll, x /= 10ll;} while (x);while (top) putchar(sta[--top] + 48);}
//}
//using FIO::read;using FIO::write;
const int N=2e5+7;
int n,m,ans[N],tr[N],K;
struct node{int x,y,z,cnt,ans;}a[N],b[N];
bool operator == (const node &x,const node &y){return x.x==y.x&&x.y==y.y&&x.z==y.z;}
bool cmp1(const node &x,const node &y){return x.x==y.x?(x.y==y.y?x.z<y.z:x.y<y.y):x.x<y.x;}
bool cmp2(const node &x,const node &y){return x.y==y.y?x.z<y.z:x.y<y.y;}
void modify(int x,int w){while(x<=K)tr[x]+=w,x+=x&-x;}
int query(int x){int res=0;while(x)res+=tr[x],x-=x&-x;return res;}
void solve(int ql,int qr){
if(ql==qr)return;
int mid=(ql+qr)>>1;
solve(ql,mid),solve(mid+1,qr);
sort(a+ql,a+mid+1,cmp2),sort(a+mid+1,a+qr+1,cmp2);
int xl=ql;
for(int yl=mid+1;yl<=qr;yl++){
while(xl<=mid&&a[xl].y<=a[yl].y)modify(a[xl].z,a[xl].cnt),xl++;
a[yl].ans+=query(a[yl].z);
}
for(int i=ql;i<xl;i++)modify(a[i].z,-a[i].cnt);
}
bool Med;
signed main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
cin>>n>>K;
for(int i=1;i<=n;i++){int x,y,z;cin>>x>>y>>z;b[i]={x,y,z,1,0};}
sort(b+1,b+n+1,cmp1);
m=0;
for(int l=1;l<=n;l++){
int r=l;while(r<n&&b[l]==b[r+1])r++;
int num=r-l+1;a[++m]={b[l].x,b[l].y,b[l].z,num,0};
l=r;
}
sort(a+1,a+m+1,cmp1);
solve(1,m);
for(int i=1;i<=m;i++)ans[a[i].ans+a[i].cnt-1]+=a[i].cnt;
for(int i=0;i<n;i++)cout<<ans[i]<<'\n';
cerr<<'\n'<<1e3*clock()/CLOCKS_PER_SEC<<"ms\n";
cerr<<'\n'<<fabs(&Med-&Mbe)/1048576.0<<"MB\n";
return 0;
}

浙公网安备 33010602011771号