CDQ分治复习

Posted Harris-H

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CDQ分治复习相关的知识,希望对你有一定的参考价值。

CDQ分治复习

1.二维区间翻转、二维区间求和

可以转化为三维偏序,第一维是按时间顺序,因为题意是按顺序给的,所以第一维解决,接下来第二维是x,第三维是y。

第二维采用归并排序很容易,第三维用BIT维护。

这里BIT我们维护差分数组。

区间求和

区间求和可以拆成4个前缀和的容斥。

然后前缀和可以用差分数组表示。

通过4个BIT我们就可以logn实现修改和查询了。

区间翻转

区间翻转其实等价于模2下的区间加法。

而区间加法可以用差分数组直接实现。

因为我们前面维护的4个BIT,所以这里的W也要乘上对应的参数(1,i,j,ij)

时间复杂度: O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull; 
const int N=1e5+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
const int hashmod[4] = 402653189,805306457,1610612741,998244353;
#define mst(a,b) memset(a,b,sizeof a)
#define db double
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define x first
#define y second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define per(i,a,b) for(int i=a;i>=b;--i)
#define ios ios::sync_with_stdio(false),cin.tie(nullptr) 
void Print(int *a,int n)
	for(int i=1;i<n;i++)
		printf("%d ",a[i]);
	printf("%d\\n",a[n]); 

template <typename T>		//x=max(x,y)  x=min(x,y)
void cmx(T &x,T y)
	if(x<y) x=y;

template <typename T>
void cmn(T &x,T y)
	if(x>y) x=y;

struct node
	int x,y,f,id;
a[N<<2],a2[N<<2];
int n,q,nq,Q;
int ans[N];
struct BIT
	#define lowbit(x) x&(-x)
	#define il inline
	ll s[N];
	il void add(int x,int v)
		while(x<=1e5)
			s[x]+=v;x+=lowbit(x);
		return;
	
	il ll que(int x)
		ll ans=0;
		while(x)
			ans^=s[x];x-=lowbit(x);
		return ans;
	
A,Ai,Aj,Aij;
void cdq(int l,int r)
	//printf("(%d,%d)\\n",l,r);
	if(l==r) return;
	int m=l+r>>1;
	cdq(l,m),cdq(m+1,r);
	int L=l,R=m+1;
	for(;R<=r;R++)
		if(a[R].id)
			for(;L<=m&&a[L].x<=a[R].x;L++) 
			if(!a[L].id)
				A.add(a[L].y,a[L].f);
				Ai.add(a[L].y,a[L].x*a[L].f);
				Aj.add(a[L].y,a[L].y*a[L].f);
				Aij.add(a[L].y,1LL*a[L].x*a[L].y*a[L].f);
			
			int x = a[R].x,y=a[R].y;
			ans[a[R].id]+=(1LL*A.que(y)*(1LL*x*y+x+y+1)*a[R].f)%2;
			ans[a[R].id]+=(1LL*Ai.que(y)*(y+1)*a[R].f)%2;
			ans[a[R].id]+=(1LL*Aj.que(y)*(x+1)*a[R].f)%2;
			ans[a[R].id]+=(1LL*Aij.que(y)*a[R].f)%2;
		
	for(int i=l;i<L;i++) if(!a[i].id)
				A.add(a[i].y,-a[i].f);
				Ai.add(a[i].y,-a[i].x*a[i].f);
				Aj.add(a[i].y,-a[i].y*a[i].f);
				Aij.add(a[i].y,-1LL*a[i].x*a[i].y*a[i].f);
	
	L=l,R=m+1;
	for(int i=l;i<=r;i++)
		if(R>r||(L<=m&&a[L].x<=a[R].x)) a2[i]=a[L++];
		else a2[i]=a[R++];
	for(int i=l;i<=r;i++) a[i]=a2[i];

int main()
	scanf("%d%d",&n,&q);
	rep(i,1,q)
		int op,x1,y1,x2,y2;
		scanf("%d%d%d%d%d",&op,&x1,&y1,&x2,&y2);
		
		if(op==1)
			a[++nq]=x1,y1,1,0;
			a[++nq]=x2+1,y1,-1,0;
			a[++nq]=x1,y2+1,-1,0;
			a[++nq]=x2+1,y2+1,1,0;
		else if(op==2)
			++Q;
			a[++nq]=x2,y2,1,Q;
			a[++nq]=x2,y1-1,-1,Q;
			a[++nq]=x1-1,y2,-1,Q;
			a[++nq]=x1-1,y1-1,1,Q;			
		
	
	cdq(1,nq);
	rep(i,1,Q) printf("%d\\n",ans[i]&1);
	return 0;


总结

cdq分治是解决多维偏序的算法。

许多区间统计问题可以通过容斥转化为偏序问题(也就是多维前缀和),这也是为什么最后一维通常可以用BIT进行维护的原因。

以上是关于CDQ分治复习的主要内容,如果未能解决你的问题,请参考以下文章

算法复习——cdq分治

BZOJ 3262: 陌上花开 cdq分治 树状数组

BZOJ 2244: [SDOI2011]拦截导弹 [CDQ分治 树状数组]

hdu5618(cdq分治求三维偏序)

P4093 [HEOI2016/TJOI2016]序列 (CDQ分治)

HDU5730 FFT+CDQ分治