CDQ 分治

CDQ 分治可以处理一些点对 \((i, j)\),将其分为左区间和右区间,并统计左右区间之间的贡献

P3810 【模板】三维偏序 / 陌上花开

这题要注意去重(好麻烦哦)
先将 \(a\) 排序,然后 CDQ 分治
分治时要保证区间 \((l, r)\)\(b\) 是有序的,虽然 \(a\) 被打乱了,但是左区间任意的 \(a\) 还是小于右区间任意的 \(a\)
统计答案的时候仿照归并排序,然后维护树状数组

复杂度是 \(O(n \ logn \ logk)\),离散化以后是 \(O(n \ log^2n)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define debug(x) cout<<#x<<" = "<<x<<endl;
const int N = 1e5+5, E = 2e5+5;
int n, k, cnt, tree[E], ans[N], bucket[N], t[N];
struct Element{
	int a, b, c, id, num;
}tmp[N], a[N];
inline ll read(){
	ll s=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){s=(s<<1)+(s<<3)+(c^48);c=getchar();}
	return s*f;
}
bool cmp(Element x, Element y){
	if(x.a != y.a)return x.a < y.a;
	if(x.b != y.b)return x.b < y.b;
	return x.c < y.c;
}
void add(int x, int c){
	for(;x<=k;x+=x&-x)tree[x] += c;
}
int query(int x){
	int ans = 0;
	for(;x;x-=x&-x)ans += tree[x];
	return ans;
}
void CDQ(int l, int r){
	if(l == r)return;
	int mid = l + r >> 1;
	CDQ(l, mid);
	CDQ(mid+1, r);
	int pos1 = l, pos2 = mid + 1, pos = l;
	while(pos1 <= mid || pos2 <= r){
		if(pos2 > r || pos1 <= mid && a[pos1].b <= a[pos2].b){
			add(a[pos1].c, a[pos1].num);
			tmp[pos++] = a[pos1++];
		}else{
			ans[a[pos2].id] += query(a[pos2].c);
			tmp[pos++] = a[pos2++];
		}
	}
	for(int i=l;i<=mid;i++)add(a[i].c, -a[i].num);
	for(int i=l;i<=r;i++)a[i] = tmp[i];
}
int main(){
	n = read(), k = read();
	for(int i=1;i<=n;i++)tmp[i] = Element{read(), read(), read()};
	sort(tmp+1, tmp+n+1, cmp);
	for(int i=1;i<=n;i++){
		if(tmp[i].a != tmp[i-1].a || tmp[i].b != tmp[i-1].b || tmp[i].c != tmp[i-1].c)a[++cnt] = tmp[i];
		a[cnt].num++;
		a[cnt].id = cnt;
		t[cnt] ++;//CDQ之后顺序会打乱,还要再记录一次点对数量 
	}
	CDQ(1, cnt);
	for(int i=1;i<=cnt;i++)ans[i] += t[i]-1, bucket[ans[i]] += t[i];
	for(int i=0;i<n;i++)cout << bucket[i] << endl;
	return 0;
}

\(\,\)

P4390 [BalkanOI 2007] Mokia 摩基亚

这篇题解好好呀,好喜欢这样的题解,既清楚又有趣
很多细节放代码里了xixi

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define debug(x) cout<<#x<<" = "<<x<<endl;
const int N = 2e5 + 5, E = 2e6+5;
int w, cnt, qcnt, opt, ans[10005], tree[E];
struct Element{
	int x, y, opt, val, f, id;
}a[N], tmp[N];
inline ll read(){
	ll s=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){s=(s<<1)+(s<<3)+(c^48);c=getchar();}
	return s*f;
}
bool cmp(Element a, Element b){
	if(a.id != b.id)return a.id < b.id; // 按时间戳排序,这样可以保证统计的答案都是合法的 
	return a.opt > b.opt; // 将询问操作排在前面
}
void add(int x, int k){
	for(;x<=w;x+=x&-x)tree[x] += k;
}
int query(int x){
	int ans = 0;
	for(;x;x-=x&-x)ans += tree[x];
	return ans;
}
void CDQ(int l, int r){
	if(l == r)return;
	int mid = l + r >> 1;
	CDQ(l, mid);
	CDQ(mid+1, r);
	int pos = l, pos1 = l, pos2 = mid+1;
	while(pos <= r){
		if(pos2 > r || pos1 <= mid && a[pos1].x <= a[pos2].x){
			if(a[pos1].opt == 1)add(a[pos1].y, a[pos1].val);
			tmp[pos++] = a[pos1++];
		}else{
			if(a[pos2].opt == 2)ans[a[pos2].id] += a[pos2].f * query(a[pos2].y);
			tmp[pos++] = a[pos2++];
		}
	}
	for(int i=l;i<=mid;i++)if(a[i].opt == 1)add(a[i].y, -a[i].val);
	for(int i=l;i<=r;i++)a[i] = tmp[i];
}
int main(){
	opt = read(), w = read() + 1;
	// w x y 整体 + 1,防止树状数组取 0 
	while(opt = read()){
		if(opt == 3)break;
		if(opt == 1)a[++cnt] = {read() + 1, read() + 1, 1, read(), 0, qcnt};
		if(opt == 2){
			int xa = read() + 1, ya = read() + 1, xb = read() + 1, yb = read() + 1;
			a[++cnt] = {xa-1, ya-1, 2, 0, 1, ++qcnt};
			a[++cnt] = {xb, ya-1, 2, 0, -1, qcnt};
			a[++cnt] = {xa-1, yb, 2, 0, -1, qcnt};
			a[++cnt] = {xb, yb, 2, 0, 1, qcnt};
		}
	}
	sort(a+1, a+cnt+1, cmp);
	CDQ(1, cnt);
	for(int i=1;i<=qcnt;i++)cout << ans[i] << endl;
	return 0;
}
posted @ 2026-01-15 12:52  今添  阅读(1)  评论(0)    收藏  举报