CDQ分治

感觉就是整体二分,只不过重命名了。
所以说一下它的实现方式。
套路是定下来一个区间,统一计算左区间端点对右区间的贡献(左右可颠倒)。
然后一直递归到l==r这时跨过每个节点的答案均被统计,不重不漏。
这里放几个模板题:
寒假作业
最纯的分治,实现特别简单,分治,然后排序进行左对右的统计即可。
这里不放码了,占博客空间。
三维偏序
这个题的套路是排序与去重。
考虑去计算贡献,首先要先将一维保证有序。
所以整体按照a为第一关键字,b为第二关键字,c为第三关键字排序。
然后我们用CDQ,考虑跨过中点之后,右端点固定所能够有的贡献。
发现二分后左边的a小于右边的a,所以左右分别按照b排序。
然后将右边界指针按b从小到大扫,同时左边比右指针小的点都有可能造成贡献。
造成贡献当且仅当左边节点的c值<=右指针c值。
所以把这个事情交给树状数组去统计即可。
注意三个值均相同的元素去统计时会出问题,举个极端例子,整个区间两个节点,这俩相同。
这时候你整体二分再统计,得到答案为1的点只有一个,但事实上它有两个答案为1的点。
所以这个时候就要给端点赋值再CDQ统计。

#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
#define qr qr()
#define ps push_back
#define pa pair<int,int>
#define ve vector
#define fi first
#define se second
using namespace std;
const int N=2e5+200;
int n,m,c[N],st[N],top,cnt[N],ans[N],tot;
inline ll qr{
	ll x=0;char ch=getchar();
	while(ch>57||ch<48)ch=getchar();
	while(ch>=48&&ch<=57)x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return x;
}
struct node{
	int a,b,c,ans,val;
}w[N];
bool cmp(node a,node b){
	return (a.a==b.a)?((a.b==b.b)?a.c<b.c:a.b<b.b):a.a<b.a;
}
bool cmp2(node a,node b){
	return a.b<b.b;
}
inline int ask(int pos){
	int res=0;
	while(pos)res+=c[pos],pos-=pos&-pos;
	return res;
}
inline void update(int pos,int val){
	while(pos<=m)c[pos]+=val,pos+=pos&-pos;
}
void solve(int l,int r){
	if(l==r)return;
	int md=l+r>>1;
	solve(l,md);solve(md+1,r);
	sort(w+l,w+md+1,cmp2);
	sort(w+md+1,w+r+1,cmp2);
	top=0;
	for(int i=md+1,j=l;i<=r;++i){
		while(w[j].b<=w[i].b&&j<=md)update(w[j].c,w[j].val),st[++top]=j,++j;
		w[i].ans+=ask(w[i].c);
	}for(int i=1;i<=top;++i)update(w[st[i]].c,-w[st[i]].val);
}
void init(){
	n=qr;m=qr;
	for(int i=1,a,b,c;i<=n;++i)
		a=qr,b=qr,c=qr,w[i]={a,b,c};
	sort(w+1,w+n+1,cmp);
	for(int i=1,c=0;i<=n;++i){
		++c;
		if(w[i].a!=w[i+1].a||w[i].b!=w[i+1].b||w[i].c!=w[i+1].c)
			w[++tot]=w[i],w[tot].val=c,c=0;
	}
	sort(w+1,w+tot+1,cmp);
	solve(1,tot);
	for(int i=1;i<=tot;++i)
		cnt[w[i].ans+w[i].val-1]+=w[i].val;
	for(int i=0;i<n;++i)
		cout<<cnt[i]<<'\n';
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);




	freopen("in.in","r",stdin);
	freopen("out.out","w",stdout);




	init();
	return 0;
}
posted @ 2024-08-15 14:55  SLS-wwppcc  阅读(9)  评论(0)    收藏  举报