cdq分治

分析

  • 将询问和查询拆开,有的时候有奇效(即使是只有查询的题目)

  • 可以存在撤销操作,删除时需要考虑后面删掉了前面的情况

  1. 如果删除很简单,则没啥关系

  2. 如果删除很难,则可以采用时间回溯,先把所有删除操作做完,从后往前做,变成插入,时间复杂度一个\(lg\),如果插入和询问还需要分开时,时间复杂度再加一个\(lg\) ,但我感觉这个真和线段树分治没啥区别,也就是没啥用?

例题1

Luogu P6406 [COCI2014-2015#2] Norma

数据范围卡掉了万能的分块,考虑分治

只需要考虑跨过中线的答案

对于每个左边的点,右边可以分成3部分(如图分别表示max和min的位置)

显然,随着枚举的左边界向左移动,左边的max只会变大,min只会减小,而右边的两条分割线也会随之右移

对于每个左边界,计算它到r之间全部的答案

  1. 对于第一块左左,左边的max\(\times\)左边的min\(\times\)区间大小和
  2. 第二块左右,设max在左边,左边的max\(\times\)(右边的min\(\times\)区间大小和)=左边max\(\times\)(位于右边的块内的min和+左边块的大小\(\times\)右边的min和)
  3. 第三块右右,右边的max\(\times\)右边的min\(\times\)区间大小和=左边块的大小\(\times\)右边的maxmin和+右边块内的maxmin和

需要预处理右边mx的前缀和,右边mx的块的前缀和,右边mn的前缀和,右边mn的块的前缀和,右边mxmn的前缀和,右边mxmn的块的前缀和

\(O(nlgn)\)

WA了一次:没思考清楚左左时的区间大小和

#include<bits/stdc++.h>
#define ll long long
const int p=1e9;
using namespace std;

const int N=5e5+5;
int n,a[N],mx[N],mn[N],s0[N],s1[N],s2[N],t0[N],t1[N],t2[N];
int ans;
inline int mo(int x) {
	return x>=p?x-p:x;
}
void work(int l,int r) {
	if(l==r) {
		ans=((ll)a[l]*a[l]+ans)%p;
		return;
	}
	int mid=l+r>>1;
	work(l,mid),work(mid+1,r);
	int top=1;
	mx[top]=mn[top]=s0[top]=t0[top]=s1[top]=t1[top]=a[mid+1];
	t2[top]=s2[top]=(ll)a[mid+1]*a[mid+1]%p;
	for(int i=mid+2;i<=r;i++) {
		top++;
		mx[top]=max(mx[top-1],a[i]);
		mn[top]=min(mn[top-1],a[i]);
		t0[top]=mo(t0[top-1]+mx[top]);
		s0[top]=((ll)s0[top-1]+(ll)mx[top]*(i-mid))%p;
		t1[top]=mo(t1[top-1]+mn[top]);
		s1[top]=((ll)s1[top-1]+(ll)mn[top]*(i-mid))%p;
		t2[top]=((ll)t2[top-1]+(ll)mn[top]*mx[top])%p;
		s2[top]=((ll)s2[top-1]+(ll)mn[top]*mx[top]%p*(i-mid))%p;
	}
	int zd=0,zx=p,num1=0,num2=1;
	for(int i=mid;i>=l;i--) {
		zd=max(zd,a[i]),zx=min(zx,a[i]);
		while(num1<top&&zd>=mx[num1+1]&&zx<=mn[num1+1])  num1++;
		while(num2<=top&&!(zd<mx[num2]&&zx>mn[num2])) num2++;
		if(num1) ans=((ll)ans+(ll)zd*zx%p*(((ll)(num1+mid-i+mid-i+3)*num1/2)%p))%p;
		if(num1<num2-1) {
			if(mx[num2-1]>zd) {
				ans=((ll)ans+(ll)zx*((ll)(t0[num2-1]-t0[num1]+p)*(mid-i+1)%p+s0[num2-1]-s0[num1]+p))%p;
			} else {
				ans=((ll)ans+(ll)zd*((ll)(t1[num2-1]-t1[num1]+p)*(mid-i+1)%p+s1[num2-1]-s1[num1]+p))%p;
			}
		}
		if(num2<=top) ans=((ll)ans+(ll)(t2[top]-t2[num2-1]+p)*(mid-i+1)+s2[top]-s2[num2-1]+p)%p;
	}
}
int main() {
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	work(1,n);
	printf("%d\n",ans);
	return 0;
}

模板

ybtoj G. 数星星

四维偏序

#include<bits/stdc++.h>
using namespace std;

const int N=5e4+5,M=4e5+5;
int n,m,q,ans[N];
struct A{int fl,x,y,z,id; }h[M],b[M],a[M],t[M];
void work3(int l,int r) {
	if(l==r) return;	
	int mid=l+r>>1;
	work3(l,mid),work3(mid+1,r);
	int i=l,j=mid+1,k=l,num=0;
	for(;i<=mid&&j<=r;k++) {
		if(h[i].z<=h[j].z) {
			b[k]=h[i];
			if(h[i].fl==0) num++;
			i++;
		} else {
			b[k]=h[j];
			if(h[j].fl) ans[h[j].id]+=h[j].fl==1?num:-num;
			j++;
		}
	}
	for(;i<=mid;k++,i++) {
		b[k]=h[i];
	}
	for(;j<=r;k++,j++) {
		b[k]=h[j];
		if(h[j].fl) ans[h[j].id]+=h[j].fl==1?num:-num;
	}
	for(int i=l;i<=r;i++) h[i]=b[i];
}
void work2(int l,int r) {
	if(l==r) return;
	int mid=l+r>>1;
	work2(l,mid),work2(mid+1,r);
	int i=l,j=mid+1,k=l,top=0;
	for(;i<=mid&&j<=r;k++) {
		if(t[i].y<=t[j].y) {
			b[k]=t[i];
			if(t[i].fl==0) {
				h[++top]=t[i];
			}
			i++;
		} else {
			b[k]=t[j];
			if(t[j].fl) {
				h[++top]=t[j];
			}
			j++;
		}
	}
	for(;i<=mid;k++,i++) {
		b[k]=t[i];
	}
	for(;j<=r;k++,j++) {
		b[k]=t[j];
		if(t[j].fl) h[++top]=t[j]; 
	}
	for(int i=l;i<=r;i++) t[i]=b[i];
	if(top) work3(1,top); 
}
void work1(int l,int r) {
	if(l==r) return;
	int mid=l+r>>1;
	work1(l,mid),work1(mid+1,r);
	int i=l,j=mid+1,k=l,top=0;
	for(;i<=mid&&j<=r;k++) {
		if(a[i].x<=a[j].x) {
			b[k]=a[i];
			if(a[i].fl==0) {
				t[++top]=a[i];
			}
			i++;
		} else {
			b[k]=a[j];
			if(a[j].fl) {
				t[++top]=a[j];
			}
			j++;
		}
	}
	for(;i<=mid;k++,i++) {
		b[k]=a[i];
	}
	for(;j<=r;k++,j++) {
		b[k]=a[j];
		if(a[j].fl) {
			t[++top]=a[j];
		}
	}
	for(int i=l;i<=r;i++) a[i]=b[i];
	if(top) work2(1,top);
}
int main() {
	int T; scanf("%d",&T);
	while(T--) {
		scanf("%d",&n); m=0,q=0;
		for(int i=1;i<=n;i++) {
			int op; scanf("%d",&op);
			if(op==1) {
				int x,y,z; scanf("%d%d%d",&x,&y,&z);
				a[++m]=(A){0,x,y,z,0}; 
			} else {
				int x1,y1,z1,x2,y2,z2; scanf("%d%d%d%d%d%d",&x1,&y1,&z1,&x2,&y2,&z2);
				q++;
				a[++m]=(A){1,x2,y2,z2,q};
				if(x1>1) a[++m]=(A){-1,x1-1,y2,z2,q};
				if(y1>1) a[++m]=(A){-1,x2,y1-1,z2,q};
				if(z1>1) a[++m]=(A){-1,x2,y2,z1-1,q};
				if(x1>1&&y1>1) a[++m]=(A){1,x1-1,y1-1,z2,q};
				if(x1>1&&z1>1) a[++m]=(A){1,x1-1,y2,z1-1,q};
				if(y1>1&&z1>1) a[++m]=(A){1,x2,y1-1,z1-1,q};
				if(x1>1&&y1>1&&z1>1) a[++m]=(A){-1,x1-1,y1-1,z1-1,q}; 
			}
		}
		work1(1,m);
		for(int i=1;i<=q;i++) {
			printf("%d\n",ans[i]);
		}
		memset(ans,0,sizeof(ans));
	}
	return 0;
}
posted @ 2021-04-02 11:02  wwwsfff  阅读(87)  评论(0)    收藏  举报