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;
}
posted @ 2025-11-14 21:15  all_for_god  阅读(7)  评论(0)    收藏  举报