分块算法相关

orz hhw posted @ 2015年7月26日 18:05 in 算法学习 with tags 数据结构 模板 算法学习 块状链表 bzoj 莫队算法 块状树 , 4382 阅读

一直不会带根号的神奇的算法,今天看了一下论文PPT,总算看懂块状链表大致怎么弄了,然后才看了分块、莫队和块状树,感觉也不是很难。。

一、分块

基本的分块就是维护每个块内的信息,然后求的时候把散的和整块的分开来求救可以了

BZOJ2957维护块内单调性

#include<cstdio>
#include<algorithm>
const int BS=500,BN=200;
int i,n,m,x,y,p,b,ans,l,r,mid,top[BN+9];
double k,now,data[BN+9][BS+9],stack[BN+9][BS+9];
int main(){
	scanf("%d%d",&n,&m);
	while(m--){
		scanf("%d%d",&x,&y);k=(double)y/x;
		p=(x-1)%BS+1;b=(x-1)/BS+1;
		for(data[b][p]=k,top[b]=0,now=0,i=1;i<=BS;i++)
			if(data[b][i]>now)stack[b][++top[b]]=now=data[b][i];
		for(ans=0,now=0,i=1;i<=BN;i++){
			l=1;r=top[i];
			while(l<=r){
				mid=(l+r)>>1;
				if(now<stack[i][mid])r=mid-1;else l=mid+1;
			}
			ans+=top[i]-l+1;now=std::max(now,stack[i][top[i]]);
		}
		printf("%d\n",ans);
	}
}

BZOJ2141块套树状数组统计逆序对,MD竟然有相同的数 比树套树不知道高到哪里去了

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=20010,BS=200,BN=100;
int n,m,i,bx,by,x,y,ans,top,c[BN+9][N],a[N];
struct EG{int x,z;}eg[N];
bool cmp(EG a,EG b){return a.x<b.x;}
void change(int w,int x,int z){for(;x<=n;x+=x&-x)c[w][x]+=z;}
int query(int w,int x){int tmp=0;for(;x;x-=x&-x)tmp+=c[w][x];return tmp;}
void go(int j){
	 if(a[j]<a[x])--ans;if(a[j]>a[x])++ans;  
     if(a[j]<a[y])++ans;if(a[j]>a[y])--ans;
}
int main(){
	scanf("%d",&n);
	for(i=1;i<=n;i++)scanf("%d",&eg[i].x),eg[i].z=i;
	sort(eg+1,eg+n+1,cmp);
	for(i=1;i<=n;i++){
		if(eg[i].x!=eg[i-1].x)++top;
        a[eg[i].z]=top;
    }
	for(i=n;i;i--)ans+=query(0,a[i]-1),change(0,a[i],1);
	for(i=1;i<=n;i++)change((i-1)/BS+1,a[i],1);
	printf("%d\n",ans);scanf("%d",&m);
	while(m--){
		scanf("%d%d",&x,&y);if(x>y)swap(x,y);
		bx=(x-1)/BS+1;by=(y-1)/BS+1;
		if(bx+1<=by-1){
			for(i=bx+1;i<by;i++){
				ans-=query(i,a[x]-1);ans+=query(i,n)-query(i,a[x]);
				ans+=query(i,a[y]-1);ans-=query(i,n)-query(i,a[y]);
			}
			for(i=x+1;i<=bx*BS;i++)go(i);
			for(i=by*BS-BS+1;i<y;i++)go(i);	
		}else for(i=x+1;i<y;i++)go(i);
		if(a[x]<a[y])ans++;if(a[x]>a[y])ans--;
		printf("%d\n",ans);change(bx,a[x],-1);change(by,a[y],-1);
		swap(a[x],a[y]);change(bx,a[x],1);change(by,a[y],1);
	}
}

BZOJ3295和上一题差不多,本来觉得可以水过,但谁知道这个是删除值不是删除位置,样例还看不出来。。觉得这么SB的题应该不会错,再看了遍题目才发现这个坑点。。

#include<cstdio>
const long long N=100010,BS=2000,BN=50;
long long n,m,ans,x,b,i,a[N],c[51][N],to[N];bool use[N];
long long min(long long x,long long y){return x<y?x:y;}
void change(long long w,long long x,long long z){for(;x<=n;x+=x&-x)c[w][x]+=z;}
long long query(long long w,long long x){long long tmp=0;for(;x;x-=x&-x)tmp+=c[w][x];return tmp;}
int main(){
	scanf("%lld%lld",&n,&m);
	for(i=1;i<=n;i++)scanf("%lld",&a[i]),to[a[i]]=i,ans+=query(0,n)-query(0,a[i]),change(0,a[i],1);
	for(i=1;i<=n;i++)change((i-1)/BS+1,a[i],1);
	while(m--){
		printf("%lld\n",ans);scanf("%lld",&x);
		x=to[x];b=(x-1)/BS+1;
		for(i=1;i<b;i++)ans-=query(i,n)-query(i,a[x]);
		for(i=b+1;i<=BN;i++)ans-=query(i,a[x]-1);
		for(i=(b-1)*BS+1;i<x;i++)if(a[i]>a[x]&&!use[i])ans--;
		for(i=min(b*BS,n);i>x;i--)if(a[i]<a[x]&&!use[i])ans--;
		change(b,a[x],-1);use[x]=1;
	}
}

BZOJ2821 卡了无数次常数,牺牲了ojkiller2,总算卡过去了。。。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100005,BS=250,BN=400;
int n,g,m,tot,ans,x,y,bx,by,i,j,w,top,st,ed,l,r,mid,tmp,first[N],last[N],now[N],a[N],v[N],c[N],q[N],d[403][403];
char ch;struct EG{int x,z;}eg[N];
bool cmp(EG a,EG b){return a.x<b.x||(a.x==b.x&&a.z<b.z);}
void read(int &x){
    for(ch=getchar();ch<'0';ch=getchar());
    for(x=0;ch>='0';ch=getchar())x=x*10+ch-'0';
}
int main(){
	read(n);read(g);read(m);
	for(i=1;i<=n;i++)read(a[i]),eg[i].x=a[i],eg[i].z=i;
	for(j=1;j<=BN;j++){
		memset(now,0,sizeof(now));tot=0;
		for(i=(j-1)*BS+1;i<=n;i++){
			if(!(now[a[i]]&1)&&now[a[i]])tot--;
			now[a[i]]++;if(!(now[a[i]]&1))tot++;
			d[j][(i-1)/BS+1]=tot;
		}
	}
	sort(eg+1,eg+n+1,cmp);
	for(i=1;i<=n;i++){
		if(!first[eg[i].x])first[eg[i].x]=i;
		last[eg[i].x]=i;
	}
	for(w=1;w<=m;w++){
		read(x);read(y);x=(x+ans)%n+1;y=(y+ans)%n+1;
		if(x>y)swap(x,y);bx=(x-1)/BS+1;by=(y-1)/BS+1;top=0;
		if(bx+1<=by-1){
			ans=d[bx+1][by-1];
			for(i=x;i<=bx*BS;i++)if(v[a[i]]!=w)v[a[i]]=w,c[a[i]]=1,q[++top]=a[i];else c[a[i]]++;
			for(i=(by-1)*BS+1;i<=y;i++)if(v[a[i]]!=w)v[a[i]]=w,c[a[i]]=1,q[++top]=a[i];else c[a[i]]++;
			for(i=1;i<=top;i++){
				st=bx*BS+1;ed=by*BS-BS;
				l=first[q[i]];r=last[q[i]]+1;
				while(l<r){
					mid=(l+r)>>1;
					if(st<=eg[mid].z)r=mid;else l=mid+1;
				}
				tmp=l;
				l=first[q[i]];r=last[q[i]]+1;
				while(l<r){
					mid=(l+r)>>1;
					if(ed<eg[mid].z)r=mid;else l=mid+1;
				}
				tmp=l-tmp;
				if(tmp==0&&c[q[i]]&&(c[q[i]]&1)==0)ans++;
				else if(tmp&&c[q[i]]&&(tmp&1)==0&&(c[q[i]]&1)==1)ans--;
				else if(tmp&&c[q[i]]&&(tmp&1)==1&&(c[q[i]]&1)==1)ans++;
			}
		}else for(ans=0,i=x;i<=y;i++)
				if(v[a[i]]!=w)v[a[i]]=w,c[a[i]]=1;else{
					c[a[i]]++;
					if(c[a[i]]&1)ans--;else ans++;
				}
		printf("%d\n",ans);
	}
}

BZOJ3744,d[i][j]表示第i块到第j块逆序对数,f[i][j]表示前i块比j大的数的个数,g[i][j]表示前i块比j小的数的个数,然后就可以在线求逆序对数了,时间复杂度O(n^1.5lgn)

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=50005,BS=250,BN=200;
int n,m,i,j,sum,now,ans,x,y,l,r,bx,by,b[N],c[N],a[N],v[N],d[203][203],f[203][N],g[203][N];
void add(int x){for(;x<=n;x+=x&-x)c[x]++;}
int query(int x){for(sum=0;x;x-=x&-x)sum+=c[x];return sum;}
int main(){
	scanf("%d",&n);
	for(i=1;i<=n;i++)scanf("%d",&a[i]),v[i]=a[i];
	sort(v+1,v+n+1);for(i=1;i<=n;i++)a[i]=lower_bound(v+1,v+n+1,a[i])-v;
	for(j=1;j<=BN;j++){
		memset(c,0,sizeof(c));now=0;
		for(i=(j-1)*BS+1;i<=n;i++){
			now+=query(n)-query(a[i]);
			add(a[i]);
			d[j][(i-1)/BS+1]=now;
		}
	}
	memcpy(b,a,sizeof(b));
	for(j=1;j<=BN;j++){
		l=(j-1)*BS+1;r=j*BS+1;
		sort(b+l,b+r);
		memcpy(f[j],f[j-1],sizeof(f[j]));
		memcpy(g[j],g[j-1],sizeof(g[j]));
		for(i=1;i<=n;i++){
			f[j][i]+=(b+r-upper_bound(b+l,b+r,i));
			g[j][i]+=(lower_bound(b+l,b+r,i)-b-l);
		}
	}
	scanf("%d",&m);
	while(m--){
		memset(c,0,sizeof(c));
		scanf("%d%d",&x,&y);x^=ans;y^=ans;ans=0;
		bx=(x-1)/BS+1;by=(y-1)/BS+1;
		if(bx+2<=by){
			ans=d[bx+1][by-1];
			for(i=y;i>(by-1)*BS;i--)ans+=query(a[i]-1)+f[by-1][a[i]]-f[bx][a[i]],add(a[i]);
			for(i=bx*BS;i>=x;i--)ans+=query(a[i]-1)+g[by-1][a[i]]-g[bx][a[i]],add(a[i]);
		}else for(i=y;i>=x;i--)ans+=query(a[i]-1),add(a[i]);
		printf("%d\n",ans);
	}
}

BZOJ4241 分块得到每一块答案,再维护一个前缀和,方便处理散的答案

#include<bits/stdc++.h>
#define N 100100
using namespace std;long long o,b[533][533];int n,m,i,j,w,x,y,X,Y,S=200,v[N],a[N],p[N],s[N],c[533][N];
int main(){
	for(scanf("%d%d",&n,&m),i=1;i<=n;i++)scanf("%d",&a[i]),v[i]=a[i],p[i]=(i-1)/S+1;
	for(sort(v+1,v+n+1),w=unique(v+1,v+n+1)-v,i=1;i<=n;i++)c[p[i]][a[i]=lower_bound(v+1,v+w,a[i])-v]++;
	for(i=1;i<=n;i+=S)for(o=0,memset(s,0,sizeof(s)),j=i;j<=n;j++)o=max(o,1ll*v[a[j]]*++s[a[j]]),b[p[i]][p[j]]=o;
	for(i=S+1;i<=n;i+=S)for(j=1;j<w;j++)c[p[i]][j]+=c[p[i]-1][j];
	for(;m--;printf("%lld\n",o)){
		scanf("%d%d",&x,&y);X=p[x];Y=p[y];o=b[X+1][Y-1];
		if(X==Y){
			for(i=x;i<=y;i++)s[a[i]]=0;
			for(i=x;i<=y;i++)o=max(o,1ll*v[a[i]]*++s[a[i]]);
		}else{
			for(i=x;i<=X*S;i++)s[a[i]]=c[Y-1][a[i]]-c[X][a[i]];
			for(i=Y*S-S+1;i<=y;i++)s[a[i]]=c[Y-1][a[i]]-c[X][a[i]];
			for(i=x;i<=X*S;i++)o=max(o,1ll*v[a[i]]*++s[a[i]]);
			for(i=Y*S-S+1;i<=y;i++)o=max(o,1ll*v[a[i]]*++s[a[i]]);
		}
	}
}

BZOJ4028 分块,如果两块的gcd不同了,就暴力重构/查找,相同则直接在set中找,时间复杂度O(n^1.5lgn)。

#include<bits/stdc++.h>
#define N 100100
using namespace std;set<int>p[1111];char s[9];long long z;int n,i,j,S=500,B,Q,x,y,v,A,a[N],bl[N],l[1111],r[1111],G[N],X[N];
void bt(int x){
	p[x].clear();G[l[x]]=a[l[x]];X[l[x]]=a[l[x]];p[x].insert(X[l[x]]);
	for(int i=l[x]+1;i<=r[x];i++)G[i]=__gcd(G[i-1],a[i]),p[x].insert(X[i]=X[i-1]^a[i]);
}
int main(){
	for(scanf("%d",&n),i=1;i<=n;i++)scanf("%d",&a[i]),bl[i]=(i-1)/S+1;
	for(B=(n-1)/S+1,i=1;i<=B;i++)l[i]=r[i-1]+1,r[i]=i*S,r[B]=n,bt(i);
	for(scanf("%d",&Q);Q--;)if(scanf("%s",s),s[0]=='M')scanf("%d%d",&x,&y),x++,a[x]=y,bt(bl[x]);else{
		for(scanf("%lld",&z),v=x=A=0,i=1;i<=B&&!A;v=__gcd(v,G[r[i]]),x^=X[r[i++]])if(__gcd(v,G[r[i]])!=v){for(j=l[i];j<=r[i]&&!A;j++)if(1ll*(X[j]^x)*__gcd(v,G[j])==z)A=j;}else if(z%v==0&&p[i].count((int)(z/v)^x))for(j=l[i];j<=r[i]&&!A;j++)if(1ll*(X[j]^x)*v==z)A=j;
		if(A)printf("%d\n",A-1);else puts("no");
	}
}

BZOJ1926 第一种数据按权值分块,时间复杂度O(r^2c+mc)。第二种数据使用莫队,并通过分块做到O(1)修改O(n^0.5)查询,时间复杂度O(mc^0.5)。

#include<bits/stdc++.h>
#define N 500500
using namespace std;struct P{int z,x,y;}p[40040];bool cmp(P a,P b){return a.z<b.z;}int R,C,S=1000,m,n,i,j,k,o,X1,Y1,X2,Y2,z,V,W,cnt,L,a[202][202],A[202][202][202],B[202][202][202],e[N],v[N],ans[20200],pos[N],c[N],sm[2222],ms[2222];
struct Q{int l,r,z,id;}q[20020];bool Cm(Q a,Q b){return pos[a.l]<pos[b.l]||pos[a.l]==pos[b.l]&&a.r<b.r;}
void add(int x,int z){c[x]+=z;sm[pos[x]]+=z*v[x];ms[pos[x]]+=z;}
void w1(){
	for(n=C,i=1;i<=n;i++)scanf("%d",&e[i]),v[i]=e[i],pos[i]=(i-1)/S;
	for(sort(v+1,v+n+1),cnt=unique(v+1,v+n+1)-v-1,i=1;i<=n;i++)e[i]=lower_bound(v+1,v+cnt+1,e[i])-v;
	for(i=1;i<=m;i++)scanf("%d%d%d%d%d",&V,&q[i].l,&V,&q[i].r,&q[i].z),q[i].id=i;
	for(sort(q+1,q+m+1,Cm),L=i=1,R=0;i<=m;i++){
		for(;R<q[i].r;)R++,add(e[R],1);for(;R>q[i].r;R--)add(e[R],-1);
		for(;L<q[i].l;L++)add(e[L],-1);for(;L>q[i].l;)L--,add(e[L],1);
		for(V=W=0,j=(n-1)/S;j>=0;j--)if(W+sm[j]>=q[i].z)break;else W+=sm[j],V+=ms[j];
		if(j>=0)for(k=(j+1)*S;k>=j*S+1&&W<q[i].z;k--)if(W+c[k]*v[k]>=q[i].z)V+=(q[i].z-W-1)/v[k]+1,W=q[i].z;else W+=c[k]*v[k],V+=c[k];
		if(W<q[i].z)ans[q[i].id]=0;else ans[q[i].id]=V;
	}
	for(i=1;i<=m;i++)if(ans[i])printf("%d\n",ans[i]);else puts("Poor QLW");
}
void w2(){
	for(i=1;i<=R;i++)for(j=1;j<=C;j++)scanf("%d",&a[i][j]),p[++n]=P{a[i][j],i,j};
	for(sort(p+1,p+n+1,cmp),k=1;k<=n;k++)A[(k-1)/C][p[k].x][p[k].y]=p[k].z,B[(k-1)/C][p[k].x][p[k].y]=1;
	for(k=0;k<R;k++)for(i=1;i<=R;i++)for(j=1;j<=C;j++)A[k][i][j]+=A[k][i-1][j]+A[k][i][j-1]-A[k][i-1][j-1],B[k][i][j]+=B[k][i-1][j]+B[k][i][j-1]-B[k][i-1][j-1];
	for(;m--;){
		for(scanf("%d%d%d%d%d",&X1,&Y1,&X2,&Y2,&z),W=0,k=R-1;k>=0;k--){
			o=A[k][X2][Y2]+A[k][X1-1][Y1-1]-A[k][X1-1][Y2]-A[k][X2][Y1-1];
			if(z<=o)break;z-=o;W+=B[k][X2][Y2]+B[k][X1-1][Y1-1]-B[k][X1-1][Y2]-B[k][X2][Y1-1];
		}
		if(k>=0)for(i=(k+1)*C;i>k*C&&z>0;i--)if(X1<=p[i].x&&p[i].x<=X2&&Y1<=p[i].y&&p[i].y<=Y2)z-=p[i].z,W++;
		if(V<z)puts("Poor QLW");else printf("%d\n",W);
	}
}
int main(){scanf("%d%d%d",&R,&C,&m);if(R==1)w1();else w2();}

BZOJ2128 分块,记录s[i][j][k]表示1~i,1~j,在块1~k的答案,每m分一块,总复杂度O(n^2m+qm)。

#include<bits/stdc++.h>
using namespace std;
int i,j,k,pa,pb,pc,n,m,O,Q,X1,Y1,X2,Y2,l,r,t,ans,v[255][255],s[255][255][255];long long a[3333],b[3333],c[3333];
int get(int i,int j,int O){return (a[i%pa+1]+b[i%pb+1]+c[i%pc+1]+a[j%pa+1]+b[j%pb+1]+c[j%pc+1])%O+1;}
struct P{int z,x,y;}p[66666];bool cmp(P a,P b){return a.z<b.z;}
int G(int z){
	int u,i,V=0,o;
	for(u=0;u<m;u++)if(p[u*n+1].z>z)break;
	if(p[u*n+1].z>z)u--;if(u==m)u--;
	if(u>=0){
		V=s[X2][Y2][u]+s[X1-1][Y1-1][u]-s[X2][Y1-1][u]-s[X1-1][Y2][u];o=u*n;
		for(i=2;i<=n;i++)if(p[o+i].z<=z&&p[o+i].z!=p[o+1].z)if(X1<=p[o+i].x&&p[o+i].x<=X2&&Y1<=p[o+i].y&&p[o+i].y<=Y2)V++;
	}return V;
}
int main(){
	for(scanf("%d",&pa),i=1;i<=pa;i++)scanf("%lld",&a[i]);
	for(scanf("%d",&pb),i=1;i<=pb;i++)scanf("%lld",&b[i]);
	for(scanf("%d",&pc),i=1;i<=pc;i++)scanf("%lld",&c[i]);
	for(scanf("%d%d%d%d",&n,&m,&O,&Q),i=1;i<=n;i++)for(j=1;j<=m;j++)v[i][j]=get(i,j,O),p[++t]=P{v[i][j],i,j};
	for(sort(p+1,p+t+1,cmp),i=1;i<=n;i++)for(j=1;j<=m;j++)for(k=0;k<m;k++)if(v[i][j]>p[k*n+1].z)s[i][j][k]=s[i-1][j][k]+s[i][j-1][k]-s[i-1][j-1][k];else s[i][j][k]=s[i][j-1][k]+s[i-1][j][k]-s[i-1][j-1][k]+1;
	for(i=1;i<=Q;i++){
		X1=get(i,1,n);Y1=get(i,2,m);X2=get(i,3,n);Y2=get(i,4,m);l=get(i,5,O);r=get(i,6,O);
		if(X1>X2)swap(X1,X2);if(Y1>Y2)swap(Y1,Y2);if(l>r)swap(l,r);ans^=(G(r)-G(l-1));
	}
	printf("%d",ans);
}

二、莫队算法

1.普通莫队

似乎就是[l,r]能影响到[l-1,r]和[l,r+1]时可以用,暴力分块先按左端点所在块排序,再按右端点排序

BZOJ2038:每次[l,r]改变只会影响到一个组合数,可以O(1)求出

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
const LL N=50005,BS=500;
LL n,m,i,l,r,k,ans,s[N],a[N],pos[N];
struct EG{LL a,b,l,r,z;}eg[N];
LL gcd(LL a,LL b){return b==0?a:gcd(b,a%b);}
LL sqr(LL x){return x*x;}
bool cmp(EG a,EG b){//按左端点所在块排序 
	if(pos[a.l]==pos[b.l])return a.r<b.r;
	return a.l<b.l;//所在块相同按右端点排序 
}
bool cmp2(EG a,EG b){return a.z<b.z;}
void update(int p,int add){//更新答案 
	ans-=sqr(s[a[p]]);
	s[a[p]]+=add;
	ans+=sqr(s[a[p]]);
}
int main(){
	scanf("%lld%lld",&n,&m);
	for(i=1;i<=n;i++)scanf("%lld",&a[i]),pos[i]=(i-1)/BS+1;
	for(i=1;i<=m;i++)scanf("%lld%lld",&eg[i].l,&eg[i].r),eg[i].z=i;
	sort(eg+1,eg+m+1,cmp);
	for(i=1,l=1,r=0;i<=m;i++){
		for(;r<eg[i].r;r++)update(r+1,1);//更新答案 
		for(;r>eg[i].r;r--)update(r,-1);
		for(;l<eg[i].l;l++)update(l,-1);
		for(;l>eg[i].l;l--)update(l-1,1);
		eg[i].a=ans-(eg[i].r-eg[i].l+1);//统计 
		eg[i].b=(eg[i].r-eg[i].l+1)*(eg[i].r-eg[i].l);
		k=gcd(eg[i].a,eg[i].b);
		eg[i].a/=k;eg[i].b/=k;
	}
	sort(eg+1,eg+m+1,cmp2);
	for(i=1;i<=m;i++)printf("%lld/%lld\n",eg[i].a,eg[i].b);
}

BZOJ3289莫队+树状数组,左右端点的左右移动可以用树状数组在logn的时间内维护出来

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=50005,BS=500;
unsigned int now,sum,ans[N];
int i,l,r,n,m,a[N],v[N],pos[N],c[N];
struct EG{int l,r,z;}eg[N];
bool cmp(EG a,EG b){
	if(pos[a.l]==pos[b.l])return a.r<b.r;
	return a.l<b.l;
}
void add(int x,int key){for(;x<=n;x+=x&-x)c[x]+=key;}
unsigned int query(int x){for(sum=0;x;x-=x&-x)sum+=c[x];return sum;}
int main(){
	scanf("%d",&n);for(i=1;i<=n;i++)scanf("%d",&a[i]),v[i]=a[i],pos[i]=(i-1)/BS+1;
	sort(v+1,v+n+1);for(i=1;i<=n;i++)a[i]=lower_bound(v+1,v+n+1,a[i])-v;
	scanf("%d",&m);for(i=1;i<=m;i++)scanf("%d%d",&eg[i].l,&eg[i].r),eg[i].z=i;
	sort(eg+1,eg+m+1,cmp);
	for(i=1,l=1,r=0;i<=m;i++){
		for(;r<eg[i].r;)r++,add(a[r],1),now+=r-l+1-query(a[r]);
		for(;r>eg[i].r;r--)add(a[r],-1),now-=r-l-query(a[r]);
		for(;l<eg[i].l;l++)add(a[l],-1),now-=query(a[l]-1);
		for(;l>eg[i].l;)l--,add(a[l],1),now+=query(a[l]-1);
		ans[eg[i].z]=now;
	}
	for(i=1;i<=m;i++)printf("%d\n",ans[i]);
}

树上莫队和带修莫队待填。。似乎树上莫队就是按DFS序处理询问,带修莫队就是加一个时间维度像K-D树一样搞,时间复杂度O(n^(5/3))

2.树上莫队

树上莫队大概就是用括号序列表示一颗树,一对括号相互抵消,这样就像普通莫队一样搞就行了

SPOJ10707 标记每个点的DFS序,并记录其括号序列,对于每对询问,预处理LCA,如果它们是有父子关系的,那么区间为两个括号序列的左括号,否则要把左区间改为x的右括号,得到这些位置后,按括号序列分块,现在只要在排好序的询问上统计答案

我们要求的点是u-v的所有点,这也相当于root到u的所有点^root到v的所有点^(lca(u,v)if !=u&v),这样和莫队一样让L和R一个一个走,当到达答案时判一下LCA就可以了,改变答案的时候做标记,如果走过那么标记为没走过,如果没走过标记为走过,并更新答案

#include<bits/stdc++.h>
#define N 44444
#define M 100100
using namespace std;
int n,m,i,x,y,t,tp,pt,tot,L,R,now,S=300,sum[N],to[M],pos[M],dfn[N],a[N],v[N],l[N],r[N],fir[N],ne[M],la[M],h[N],fa[N][18],ans[M];
struct Q{int l,r,id;}q[M];bool vis[N];
bool cmp(Q a,Q b){return pos[a.l]<pos[b.l]||pos[a.l]==pos[b.l]&&a.r<b.r;}
void ins(int x,int y){la[++tot]=y;ne[tot]=fir[x];fir[x]=tot;}
void dfs(int x){
	int i;dfn[x]=++tp;l[x]=++pt;to[pt]=x;
	for(i=1;i<=17;i++)fa[x][i]=fa[fa[x][i-1]][i-1];
	for(i=fir[x];i;i=ne[i])if(fa[x][0]!=la[i])fa[la[i]][0]=x,h[la[i]]=h[x]+1,dfs(la[i]);
	r[x]=++pt;to[pt]=x;
}
int lca(int x,int y){
    if(h[x]<h[y])swap(x,y);int t=h[x]-h[y],i;
    for(i=0;i<=17;i++)if(t>>i&1)x=fa[x][i];
    for(i=17;i>=0;i--)if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
    return x==y?x:fa[x][0];
}
void go(int x){
	if(vis[x])now-=(--sum[a[x]]==0);else now+=(++sum[a[x]]==1);
	vis[x]^=1;
}
int main(){
	for(scanf("%d%d",&n,&m),i=1;i<=n;i++)scanf("%d",&a[i]),v[i]=a[i];
	for(sort(v+1,v+n+1),i=1;i<=n;i++)a[i]=lower_bound(v+1,v+n+1,a[i])-v;
	for(i=1;i<n;i++)scanf("%d%d",&x,&y),ins(x,y),ins(y,x);
	for(dfs(1),i=1;i<=pt;i++)pos[i]=(i-1)/S;
	for(i=1;i<=m;i++){
		scanf("%d%d",&x,&y);if(dfn[x]>dfn[y])swap(x,y);
		t=lca(x,y);if(t==x)q[i].l=l[x];else q[i].l=r[x];
		q[i].r=l[y];q[i].id=i;
	}
	for(sort(q+1,q+m+1,cmp),L=1,R=0,i=1;i<=m;i++){
		for(;L>q[i].l;go(to[--L]));
		for(;L<q[i].l;go(to[L++]));
		for(;R>q[i].r;go(to[R--]));
		for(;R<q[i].r;go(to[++R]));
		x=to[L];y=to[R];t=lca(x,y);
		if(x!=t&&y!=t)go(t);
		ans[q[i].id]=now;
		if(x!=t&&y!=t)go(t);
	}
	for(i=1;i<=m;i++)printf("%d\n",ans[i]);
}

据说这题还能在线做,好神啊~

3.树上带修莫队

树上带修莫队就是再增加一维时间,按照左端点所在块为第一关键字,右端点所在块为第二关键字,时间为第三关键字排序,块的大小为 N^2/3,这样如果每次改动的复杂度为O(1),那么总的时间复杂度为N^5/3

WC2013糖果公园 按照树上带修莫队的方法统计出当前的答案就可以了

#include<bits/stdc++.h>
#define N 100100
using namespace std;
int n,m,k,i,x,y,t,L,R,l1,l2,tp,pt,T,tot,S=1333,las[N],c[N],v[N],w[N],h[N],s[N],l[N],r[N],dfn[N],pos[N<<1],to[N<<1],fir[N],ne[N<<1],la[N<<1],fa[N][18];
struct Q{int l,r,cur,id;}q[N];struct P{int x,pre,now;}p[N];bool vis[N];long long sum,ans[N];
bool cmp(Q a,Q b){return pos[a.l]<pos[b.l]||pos[a.l]==pos[b.l]&&pos[a.r]<pos[b.r]||pos[a.l]==pos[b.l]&&pos[a.r]==pos[b.r]&&a.cur<b.cur;}
void ins(int x,int y){la[++tot]=y;ne[tot]=fir[x];fir[x]=tot;}
void dfs(int x){
	int i;dfn[x]=++tp;l[x]=++pt;to[pt]=x;
	for(i=1;i<=17;i++)fa[x][i]=fa[fa[x][i-1]][i-1];
	for(i=fir[x];i;i=ne[i])if(fa[x][0]!=la[i])fa[la[i]][0]=x,h[la[i]]=h[x]+1,dfs(la[i]);
	r[x]=++pt;to[pt]=x;
}
int lca(int x,int y){
    if(h[x]<h[y])swap(x,y);int t=h[x]-h[y],i;
    for(i=0;i<=17;i++)if(t>>i&1)x=fa[x][i];
    for(i=17;i>=0;i--)if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
    return x==y?x:fa[x][0];
}
void go(int x){
	if(vis[x])sum-=(long long)v[c[x]]*w[s[c[x]]--];else sum+=(long long)v[c[x]]*w[++s[c[x]]];
	vis[x]^=1;
}
void cg(int x,int y){if(vis[x])go(x),c[x]=y,go(x);else c[x]=y;}
int main(){
	for(scanf("%d%d%d",&n,&m,&k),i=1;i<=m;i++)scanf("%d",&v[i]);
	for(i=1;i<=n;i++)scanf("%d",&w[i]);
	for(i=1;i<n;i++)scanf("%d%d",&x,&y),ins(x,y),ins(y,x);
	for(i=1;i<=n;i++)scanf("%d",&c[i]),las[i]=c[i];
	for(dfs(1),i=1;i<=pt;i++)pos[i]=(i-1)/S;
	for(i=1;i<=k;i++)if(scanf("%d%d%d",&t,&x,&y),t){
		if(dfn[x]>dfn[y])swap(x,y);
		q[++l1].r=l[y];q[l1].id=l1;q[l1].cur=l2;
		t=lca(x,y);if(t==x)q[l1].l=l[x];else q[l1].l=r[x];
	}else p[++l2].x=x,p[l2].pre=las[x],p[l2].now=las[x]=y;
	for(sort(q+1,q+l1+1,cmp),L=1,R=0,T=i=1;i<=l1;i++){
		for(;T<=q[i].cur;T++)cg(p[T].x,p[T].now);for(;T>q[i].cur;T--)cg(p[T].x,p[T].pre);
		for(;L>q[i].l;go(to[--L]));for(;L<q[i].l;go(to[L++]));
		for(;R>q[i].r;go(to[R--]));for(;R<q[i].r;go(to[++R]));
		x=to[L];y=to[R];t=lca(x,y);
		if(x!=t&&y!=t)go(t);ans[q[i].id]=sum;if(x!=t&&y!=t)go(t);
	}
	for(i=1;i<=l1;i++)printf("%lld\n",ans[i]);
}

BZOJ4129 和上题差不多,修改做到O(1)可以用分块思想,记录一个块中出现数的个数,这样每个询问可以n^0.5查询,总时间复杂度O(n^1.67+n^1.5)

#include<bits/stdc++.h>
#define N 50050
using namespace std;
int n,m,k,i,j,x,y,t,o,L,R,l1,l2,tp,pt,T,tot,S=150,las[N],c[N],ans[N],h[N],s[N],l[N],r[N],dfn[N],sm[N],pos[N<<1],to[N<<1],fir[N],ne[N<<1],la[N<<1],fa[N][16];
struct Q{int l,r,cur,id;}q[N];struct P{int x,pre,now;}p[N];bool vis[N];
bool cmp(Q a,Q b){return pos[a.l]<pos[b.l]||pos[a.l]==pos[b.l]&&pos[a.r]<pos[b.r]||pos[a.l]==pos[b.l]&&pos[a.r]==pos[b.r]&&a.cur<b.cur;}
void ins(int x,int y){la[++tot]=y;ne[tot]=fir[x];fir[x]=tot;}
void dfs(int x){
	int i;dfn[x]=++tp;l[x]=++pt;to[pt]=x;
	for(i=1;i<=15;i++)fa[x][i]=fa[fa[x][i-1]][i-1];
	for(i=fir[x];i;i=ne[i])if(fa[x][0]!=la[i])fa[la[i]][0]=x,h[la[i]]=h[x]+1,dfs(la[i]);
	r[x]=++pt;to[pt]=x;
}
int lca(int x,int y){
    if(h[x]<h[y])swap(x,y);int t=h[x]-h[y],i;
    for(i=0;i<=15;i++)if(t>>i&1)x=fa[x][i];
    for(i=15;i>=0;i--)if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
    return x==y?x:fa[x][0];
}
void go(int x){
	if(vis[x]){if(c[x]<=n)if(!--s[c[x]])sm[pos[c[x]]]--;}else if(c[x]<=n)if(!s[c[x]]++)sm[pos[c[x]]]++;
	vis[x]^=1;
}
void cg(int x,int y){if(vis[x])go(x),c[x]=y,go(x);else c[x]=y;}
int main(){
	for(scanf("%d%d",&n,&k),i=1;i<=n;i++)scanf("%d",&c[i]),las[i]=c[i];
	for(i=1;i<n;i++)scanf("%d%d",&x,&y),ins(x,y),ins(y,x);
	for(dfs(1),i=1;i<=100000;i++)pos[i]=i/S;
	for(i=1;i<=k;i++)if(scanf("%d%d%d",&t,&x,&y),t){
		if(dfn[x]>dfn[y])swap(x,y);
		q[++l1].r=l[y];q[l1].id=l1;q[l1].cur=l2;
		t=lca(x,y);if(t==x)q[l1].l=l[x];else q[l1].l=r[x];
	}else p[++l2].x=x,p[l2].pre=las[x],p[l2].now=las[x]=y;
	for(sort(q+1,q+l1+1,cmp),L=1,R=0,T=i=1;i<=l1;i++){
		for(;T<=q[i].cur;T++)cg(p[T].x,p[T].now);for(;T>q[i].cur;T--)cg(p[T].x,p[T].pre);
		for(;L>q[i].l;go(to[--L]));for(;L<q[i].l;go(to[L++]));
		for(;R>q[i].r;go(to[R--]));for(;R<q[i].r;go(to[++R]));
		x=to[L];y=to[R];t=lca(x,y);if(x!=t&&y!=t)go(t);
		for(o=0;sm[o]==S;o++);
		for(j=S*o;j<S*(o+1);j++)if(!s[j]){ans[q[i].id]=j;break;}
		if(x!=t&&y!=t)go(t);
	}
	for(i=1;i<=l1;i++)printf("%d\n",ans[i]);
}

三、块状链表

块状链表有三个基本操作

①find(p,b)找到位置p在链表的位置b

②splite(b,p)将第b块分裂成两块,前一块大小为p

③maintain(b)将b块之后的碎片合并

然后写代码就可以了。。

NOI2003 Editor :抄了一下模板,但本地一直A不了,然后纠结了好久,结果放到BZOJ上就A了。。

#include<cstdio>
#include<cstring>
#define N 2100000
#define SZ 20000//一个线性表的最大大小
#define M 333//链表的个数
int T,n,p,i,cur,pos,list[M],next[SZ],sz[SZ];
char data[M][SZ],str[N],ch[99];
int min(int x,int y){return x<y?x:y;}
int newnode(){return list[pos++];}
void delnode(int t){list[--pos]=t;}
void find(int &p,int &b){//找到位置p在链表的位置b
	for(b=0;b!=-1&&p>sz[b];b=next[b])p-=sz[b];
}
void fillblock(int b,int n,char str[],int e){//更新b的信息 
	if(b==-1)return;
	next[b]=e;sz[b]=n;
	memcpy(data[b],str,n);
}
void splite(int b,int p){//将第b块分裂成两块,前一块大小为p
	if(b==-1||p==sz[b])return;
	int t=newnode();
	fillblock(t,sz[b]-p,data[b]+p,next[b]);
	next[b]=t;sz[b]=p;
}
void maintain(int b){//将b块之后的碎片合并
	for(;b!=-1;b=next[b])
		for(int t=next[b];t!=-1&&sz[b]+sz[t]<=SZ;t=next[b]){
			memcpy(data[b]+sz[b],data[t],sz[t]);
			sz[b]+=sz[t];next[b]=next[t];
			delnode(t);
		}
}
void insert(int p,int n,char *str){//插入 
	int b,t,i;
	find(p,b);
	splite(b,p);
	for(i=0;i+SZ<=n;i+=SZ){
		t=newnode();
		fillblock(t,SZ,str+i,next[b]);
		next[b]=t;b=t;
	}
	if(n-i){
		t=newnode();
		fillblock(t,n-i,str+i,next[b]);
		next[b]=t;
	}
	maintain(b);
}
void erase(int p,int n){//删除 
	int b,t,i;
	find(p,b);
	splite(b,p);
	for(i=next[b];i!=-1&&n>sz[i];i=next[i])n-=sz[i];
	splite(i,n);i=next[i];
	for(t=next[b];t!=i;t=next[b]){
		next[b]=next[t];
		delnode(t);
	}
    maintain(b); 
}
void get(int p,int n,char *str){//输出 
	int b,t,i;
	find(p,b);
	i=min(n,sz[b]-p);
	memcpy(str,data[b]+p,i);
	for(t=next[b];t!=-1&&i+sz[t]<=n;i+=sz[t],t=next[t])
		memcpy(str+i,data[t],sz[t]);
	if(n-i&&t!=-1)memcpy(str+i,data[t],n-i);
	str[n]='\0';
}
int main(){
	for(i=1;i<M;i++)list[i]=i;
	pos=1;next[0]=-1;sz[0]=0;
	scanf("%d",&T);
	while(T--){
		scanf("%s",ch);
		if(ch[0]=='I'){
			scanf("%d",&n);
			for(i=0;i<n;i++){
				char c=getchar();
				str[i]=c;
				if(c=='\n')--i;  
			}
			insert(cur,n,str);
		}else if(ch[0]=='D')scanf("%d",&n),erase(cur,n);
		else if(ch[0]=='G')scanf("%d",&n),get(cur,n,str),printf("%s\n",str);
		else if(ch[0]=='M')scanf("%d",&cur);
		else if(ch[0]=='P')cur--;
		else if(ch[0]=='N')cur++;
	}
}

NOI2005维修数列

#include<cstdio>
#include<cstring>
#include<iostream>
const int oo=2e9,BS=700,MN=5e5+10,MB=MN/BS*2+1000;
using namespace std;
int data[MB][BS],sz[MB],sav[MB],totsum[MB],sidemax[MB][2],maxsum[MB],flist[MB],next[MB],pos;
bool same[MB],rev[MB];
int newnode(){return flist[++pos];}
void delnode(int t){flist[pos--]=t;}
void find(int &p,int &b){for(b=0;p>sz[b];b=next[b])p-=sz[b];}//找到所在的块 
void maintainblock(int b){//重新制作块 
	if(same[b]){//有相同标记 
		totsum[b]=sav[b]*sz[b];
		if(sav[b]>0)maxsum[b]=totsum[b];else maxsum[b]=sav[b];
		sidemax[b][0]=sidemax[b][1]=maxsum[b];
	}else{//没有相同标记 
		totsum[b]=0;maxsum[b]=-oo;
		for(int last=0,i=sz[b]-1;i>=0;--i){//中间最大的一段 
			totsum[b]+=data[b][i];last+=data[b][i];
			if(maxsum[b]<last)maxsum[b]=last;
			if(last<0)last=0;
		}
		sidemax[b][0]=-oo;
		for(int last=0,i=0;i<sz[b];++i){//左边起最大的一段 
			last+=data[b][i];
			if(sidemax[b][0]<last)sidemax[b][0]=last;
		}
		sidemax[b][1]=-oo;
		for(int last=0,i=sz[b]-1;i>=0;--i){//右边起最大的一段 
			last+=data[b][i];
			if(sidemax[b][1]<last)sidemax[b][1]=last;
		}
	}
}
void rever(int b){//翻转 
	if(b==-1||!rev[b])return;rev[b]=0;if(same[b])return;
	for(int l=0,r=sz[b]-1;l<r;++l,--r)swap(data[b][l],data[b][r]);
	swap(sidemax[b][0],sidemax[b][1]);
}
void maintainlist(int b){//将b以后的碎块合并 
	for(bool x=0;b!=-1;b=next[b],x=0){
		for(int t=next[b];t!=-1&&sz[b]+sz[t]<=BS;t=next[b],x=true){
			if(!(same[b]&&same[t]&&sav[b]==sav[t])){
				rever(b);rever(t);
				if(same[b])for(int i=0;i<sz[b];++i)data[b][i]=sav[b];
				for(int i=0;i<sz[t];++i)data[b][sz[b]+i]=same[t]?sav[t]:data[t][i];
				same[b]=0;
			}
			sz[b]+=sz[t];next[b]=next[t];delnode(t);
		}
		if(x)maintainblock(b);
	}
}
void fillblock(int b,int sv,int n,int e){//维护一个值相同块的信息 
	same[b]=true;sav[b]=sv;next[b]=e;
	sz[b]=n;rev[b]=0;maintainblock(b);
}
void fillblock(int b,int str[],int n,int e){//维护一个块的信息 
	same[b]=rev[b]=0;sz[b]=n;next[b]=e;
	memcpy(data[b],str,n*sizeof(int));maintainblock(b);
}
void splite(int b,int p){//将块b从p处断开 
	if(b==-1||sz[b]==p)return;
	int t=newnode();rever(b);
	if(same[b])fillblock(t,sav[b],sz[b]-p,next[b]);else fillblock(t,data[b]+p,sz[b]-p,next[b]);
	next[b]=t;sz[b]=p;maintainblock(b);
}
void insert(int p,int n,int str[]){//插入 
	int b,i,t;find(p,b);splite(b,p);
	for(i=0;i+BS<=n;i+=BS){
		t=newnode();fillblock(t,str+i,BS,next[b]);
		next[b]=t;b=t;
	}
	if(n-i){fillblock(t=newnode(),str+i,n-i,next[b]);next[b]=t;}
	maintainlist(next[b]);
}
void erase(int p,int n){//删除 
	int b,e;find(p,b);splite(b,p);
	for(e=next[b];n>sz[e];e=next[e])n-=sz[e];
	splite(e,n);e=next[e];
	for(int t=next[b];t!=e;t=next[b])next[b]=next[t],delnode(t);
}
int list[MB];
void reverse(int p,int n){//翻转 
	int b,e,i,t;find(p,b);splite(b,p);
	for(e=next[b];n>sz[e];e=next[e])n-=sz[e];
	splite(e,n);e=next[e];
	for(i=0,t=next[b];t!=e;t=next[t],++i)list[i]=t;
	next[b]=list[--i];
	for(;i>=0;--i){
		next[list[i]]=i?list[i-1]:t;
		rev[list[i]]=!rev[list[i]];
	}
	maintainlist(b);
}
void makesame(int p,int n,int v){//相同值 
	int b,t;find(p,b);splite(b,p);
	for(t=next[b];n>sz[t];n-=sz[t],t=next[t])fillblock(t,v,sz[t],next[t]);
	if(n)splite(t,n),fillblock(t,v,n,next[t]);
	maintainlist(b);
}
int getsum_in_block(int b,int p,int n){//块中总和 
	int sum,l,r;if(same[b])return sav[b]*n;
	if(rev[b]){l=sz[b]-p-n;r=sz[b]-p-1;}else{l=p;r=p+n-1;}
	for(sum=0;l<=r;++l)sum+=data[b][l];
	return sum;
}
void getsum(int p,int n){//总和 
	int b,t,sum,i;find(p,b);
	sum=getsum_in_block(b,p,min(n,sz[b]-p));n-=min(n,sz[b]-p);
	for(t=next[b]; n>sz[t]; t=next[t])sum+=totsum[t],n-=sz[t];
	sum+=getsum_in_block(t,0,n);
	printf("%d\n",sum);
}
void getmaxsum(){//最大区间和 
	int res=-oo,last=0;
	for(int b=0; b!=-1; b=next[b]){
		res=max(max(res,last+sidemax[b][rev[b]]),maxsum[b]);
		last=max(max(last+totsum[b],sidemax[b][!rev[b]]),0);
	}
	printf("%d\n",res);
}
int main(){
	int n,T,i,p,v,a[MN];char order[99];
	for(int i=1;i<MB;++i)flist[i]=i;next[0]=-1;
	scanf("%d%d",&n,&T);for(i=0;i<n;++i)scanf("%d",a+i);
	insert(0,n,a);
	while(T--){
		scanf("%s",order);
		if(order[2]=='X')getmaxsum();
		else switch(order[0]){
		case 'I':scanf("%d%d",&p,&n);for(i=0;i<n;++i)scanf("%d",a+i);insert(p,n,a);break;
		case 'D':scanf("%d%d",&p,&n);erase(p-1, n);break;
		case 'M':scanf("%d%d%d",&p,&n,&v);makesame(p-1, n ,v);break;
		case 'R':scanf("%d%d",&p,&n);reverse(p-1,n);break;
		case 'G':scanf("%d%d",&p,&n);getsum(p-1,n);break;
		}
	}
}

四、块状树

就是把树分块,每块大小为n^0.5,建块状树时只要当前块不大于n^0.5就把子节点加入,然后维护每个块的信息就行了

ZJOI2008树的统计 比树剖和LCT都短哦

#include<cstdio>
#include<cmath>
#include<algorithm>
#define inf 1e9
#define N 30003
using namespace std;char s[9];
int T,n,i,x,y,BS,tot,tot2,ans,mx,a[N],fa[N],high[N],own[N],mav[N],sum[N],fir[N],fir2[N],la[N<<1],ne[N<<1],la2[N<<1],ne2[N<<1];
void ins(int x,int y){la[++tot]=y;ne[tot]=fir[x];fir[x]=tot;}
void ins2(int x,int y){la2[++tot2]=y;ne2[tot2]=fir2[x];fir2[x]=tot2;}
void build(int x){//建立块状树 
	for(int i=fir[x];i;i=ne[i]){
		int y=la[i];
		if(fa[x]!=y){//如果没有到根号n就把子节点加入 
			if(sum[own[x]]<BS)ins2(x,y),sum[own[y]=own[x]]++;
			high[y]=high[x]+1;fa[y]=x;
			build(y);
		}
	}
}
void dfs(int x,int sm,int ma){//对每个块进行DFS 
	sum[x]=sm+=a[x];mav[x]=ma=max(ma,a[x]);
	for(int i=fir2[x];i;i=ne2[i])dfs(la2[i],sm,ma);
}
void change(int x,int y){a[x]=y;own[x]==x?dfs(x,0,-inf):dfs(x,sum[fa[x]],mav[fa[x]]);}//改变块内答案 
void Q(int x,int y){//统计 
	for(ans=0,mx=-inf;x!=y;){
		if(high[x]<high[y])swap(x,y);//改深度大的 
		if(own[x]==own[y])ans+=a[x],mx=max(mx,a[x]),x=fa[x];else{
			if(high[own[x]]<high[own[y]])swap(x,y);
			ans+=sum[x];mx=max(mx,mav[x]);x=fa[own[x]];
		}
	}
	ans+=a[x];mx=max(mx,a[x]);
}
int main(){
	scanf("%d",&n);BS=ceil(sqrt(n));
	for(i=1;i<n;i++)scanf("%d%d",&x,&y),ins(x,y),ins(y,x);
	for(i=1;i<=n;i++)scanf("%d",&a[i]),own[i]=i,sum[i]=1;
	build(1);scanf("%d",&T);
	for(i=1;i<=n;i++)if(own[i]==i)dfs(i,0,-inf);
	while(T--){
		scanf("%s%d%d",s,&x,&y);
		if(s[0]=='C')change(x,y);else{
			Q(x,y);
			if(s[1]=='S')printf("%d\n",ans);else printf("%d\n",mx);
		}
	}
}

BZOJ3720:块状树的一些操作,对每个块进行排序,加点的时候如果块大于BS就重开一个块,BS=(nlgn)^0.5时效果最好

#include<cstdio>
#include<algorithm>
#include<cmath>
#define N 60010
using namespace std;
int n,i,top,T,p,x,y,BS,ans,a[N],own[N],fa[N];
struct BL{//块的信息 
	int num[700],size;
	int ask(int k){return size-(upper_bound(num+1,num+size+1,k)-num-1);}
}blk[6000];
struct BG{//存图 
	int fir[N],tot,la[N<<1],ne[N<<1];
	void add(int x,int y){la[++tot]=y;ne[tot]=fir[x];fir[x]=tot;}
}g1,g3;
void build(int x){//建立块状树 
	int t=own[x];blk[t].num[++blk[t].size]=a[x];
	for(int i=g1.fir[x];i;i=g1.ne[i]){
		int y=g1.la[i];
		if(fa[x]!=y){//如果没有到BS就把子节点加入  
			if(blk[t].size<BS)own[y]=t;else own[y]=++top;
			fa[y]=x;
			if(t!=own[y])g3.add(t,own[y]);
			build(y);
		}
	}
}
int QB(int x,int k){//处理整块 
	int ans=blk[x].ask(k);
	for(int i=g3.fir[x];i;i=g3.ne[i])ans+=QB(g3.la[i],k);
	return ans;
}
int Q(int x,int k){//处理散点 
	int ans=a[x]>k;
	for(int i=g1.fir[x];i;i=g1.ne[i]){
		int y=g1.la[i];
		if(fa[x]!=y)if(own[x]==own[y])ans+=Q(y,k);else ans+=QB(own[y],k);
	}
	return ans;
}
int main(){
	scanf("%d",&n);BS=sqrt(n*log(n)/log(2));
	for(i=1;i<n;i++)scanf("%d%d",&x,&y),g1.add(x,y),g1.add(y,x);
	for(i=1;i<=n;i++)scanf("%d",&a[i]);
	own[1]=++top;build(1);scanf("%d",&T);
	for(i=1;i<=top;i++)sort(blk[i].num+1,blk[i].num+blk[i].size+1);
	while(T--){
		scanf("%d%d%d",&p,&x,&y);x^=ans;y^=ans;
		if(p==0)printf("%d\n",ans=Q(x,y));//询问x的子数中比y大的个数 
		else if(p==1){//修改单点权值 
			int t=own[x];
			for(int i=1;i<=blk[t].size;i++)
				if(blk[t].num[i]==a[x]){blk[t].num[i]=a[x]=y;break;}
			sort(blk[t].num+1,blk[t].num+blk[t].size+1);
		}else{//加点 
			int t=own[x];
			g1.add(x,++n);a[n]=y;fa[n]=x;
			if(blk[t].size<BS){//加到块中 
				blk[t].num[++blk[t].size]=y;
				sort(blk[t].num+1,blk[t].num+blk[t].size+1);
				own[n]=t;
			}else{//新建一个块 
				g3.add(t,++top);
				blk[top].num[++blk[top].size]=y;
				own[n]=top;
			}
		}
	}
}

待做:BZOJ3337、3731

Avatar_small
NCERT Social Questio 说:
2022年9月29日 14:17

Social Science or Social Studies including Geography, History, Civics, and Economics is called as Social in the Secondary Education Class 9th Standard Curriculum. NCERT has published learning and study material for all topics of the course to all regional students of Class 9th standard studying at Hindi Medium, NCERT Social Question Paper Class 9 English Medium and Urdu Medium Schools of the country to the academic year of 2023.Social Science or Social Studies including Geography, History, Civics, and Economics is called as Social in the Secondary Education Class 9th Standard Curriculum.


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter