动态淀粉质(划掉)题单&简要题解

简介

动态点分治的思想:还不太清楚诶怎么办。

大概是通过降低树高来降低每次修改和询问的复杂度吧,还可以把树上一个连通块的信息统计到一个点(重心)上。具体实现方式和普通的静态点分治没有太大的区别,只是把点分治时递归到的每层重心用边连起来(当然不是在原树中直接连),构成一个叫做点分树(VPT)的东西,它其实就是个弟弟递归结构。

修改原树信息时(注意这里的修改一般是围绕一个结点进行的,但不一定是单点修改),可以在点分树上找到对应的结点,然后一路爬点分树的树边修改沿路结点的信息,询问的时候和修改差不多。

但是直接这样做会有些问题,容易发现,在点分树上的一对父子结点的担当区域是有交集的(其实就是父结点的担当区域包含子结点的),所以,为了去除重复部分对每次询问答案的影响,通常需要在每个结点处再维护一个这个结点担当区域对其点分树上父结点的贡献,爬树边时把它减去就行了。

看几道例题:

[BZOJ3730]震波

传送门

分析

点分树每个结点处维护两棵线段树,一棵存这个结点担当区域的结点对这个结点的贡献,另一个存这个结点担当区域对其父结点的贡献。

这份代码会TLE。

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cctype>
#include <algorithm>
#define rin(i,a,b) for(register int i=(a);i<=(b);i++)
#define rec(i,a,b) for(register int i=(a);i>=(b);i--)
#define trav(i,a) for(register int i=head[(a)];i;i=e[i].nxt)
typedef long long LL;
using std::cin;
using std::cout;
using std::endl;

char buf[1<<23],*p1=buf,*p2=buf,obuf[1<<23],*O=obuf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}

const int MAXN=100005;
int n,m,ecnt,cog,head[MAXN],a[MAXN];
int id[MAXN],num[MAXN],pos[MAXN],dep[MAXN],st[25][MAXN<<1],tot,len;
int fa[MAXN],siz[MAXN],totsiz;
int root1[MAXN],root2[MAXN],lc[MAXN*200],rc[MAXN*200],sum[MAXN*200],loc,ql,qr,kk,tot2;
bool vis[MAXN];
struct Edge{
	int to,nxt;
}e[MAXN<<1];

inline void add_edge(int bg,int ed){
	ecnt++;
	e[ecnt].to=ed;
	e[ecnt].nxt=head[bg];
	head[bg]=ecnt;
}

void dfs(int x,int pre,int depth){
	dep[x]=depth;
	id[x]=++tot;
	num[tot]=x;
	st[0][++len]=tot;
	pos[x]=len;
	trav(i,x){
		int ver=e[i].to;
		if(ver==pre) continue;
		dfs(ver,x,depth+1);
		st[0][++len]=id[x];
	}
}

inline void buildst(){
	int lim=log2(len);
	rin(i,1,lim) rin(j,1,len-(1<<i)+1)
		st[i][j]=std::min(st[i-1][j],st[i-1][j+(1<<(i-1))]);
}

inline int lca(int x,int y){
	int xx=pos[x],yy=pos[y];
	if(xx>yy) std::swap(xx,yy);
	int lim=log2(yy-xx+1);
	return num[std::min(st[lim][xx],st[lim][yy-(1<<lim)+1])];
}

inline int getdis(int x,int y){
	return dep[x]+dep[y]-(dep[lca(x,y)]<<1);
}

void getcog(int x,int pre){
	siz[x]=1;
	bool flag=1;
	trav(i,x){
		int ver=e[i].to;
		if(ver==pre||vis[ver]) continue;
		getcog(ver,x);
		siz[x]+=siz[ver];
		if(siz[ver]>totsiz/2) flag=0;
	}
	if(totsiz-siz[x]>totsiz/2) flag=0;
	if(flag) cog=x;
}

#define mid ((l+r)>>1)
int upd(int pre,int l,int r){
	int o=pre;
	if(!o) o=++tot2;
	sum[o]+=kk;
	if(l==r) return o;
	if(loc<=mid) lc[o]=upd(lc[pre],l,mid);
	else rc[o]=upd(rc[pre],mid+1,r);
	return o;
}

int query(int o,int l,int r){
	if(!o) return 0;
	if(ql<=l&&r<=qr) return sum[o]; 
	int ret=0; 
	if(mid>=ql) ret+=query(lc[o],l,mid);
	if(mid<qr) ret+=query(rc[o],mid+1,r);
	return ret;
}
#undef mid

void buildsgt(int x,int pre,int now){
	loc=getdis(x,now)+1,kk=a[x];
	root1[now]=upd(root1[now],1,n);
	if(fa[now]){
		loc=getdis(x,fa[now])+1,kk=a[x]; 
		root2[now]=upd(root2[now],1,n);
	}
	siz[x]=1;
	trav(i,x){
		int ver=e[i].to;
		if(ver==pre||vis[ver]) continue;
		buildsgt(ver,x,now);
		siz[x]+=siz[ver];
	}
}

void buildvpt(int x){
	vis[x]=1;
	buildsgt(x,0,x);
	trav(i,x){
		int ver=e[i].to;
		if(vis[ver]) continue;
		totsiz=siz[ver];
		getcog(ver,x);
		fa[cog]=x;
		buildvpt(cog);
	}
}

inline int getans(int x,int y){
	int now=x,ret=0;
	while(1){
		int curdis=getdis(x,now);
		if(curdis<=y){
			ql=1,qr=y-curdis+1;
			ret+=query(root1[now],1,n);
		}
		if(!fa[now]) return ret;
		curdis=getdis(x,fa[now]);
		if(curdis<=y){
			ql=1,qr=y-curdis+1;
			ret-=query(root2[now],1,n);
		}
		now=fa[now];
	}
}

inline void modi(int x,int y){
	int now=x;
	while(1){
		loc=getdis(x,now)+1,kk=y-a[x];
		root1[now]=upd(root1[now],1,n);
		if(!fa[now]){a[x]=y;return;}
		loc=getdis(x,fa[now])+1,kk=y-a[x];
		root2[now]=upd(root2[now],1,n);
		now=fa[now];
	}
}
 
int main(){
	n=read(),m=read();
	rin(i,1,n) a[i]=read();
	rin(i,2,n){
		int u=read(),v=read();
		add_edge(u,v);
		add_edge(v,u);
	}
	dfs(1,0,1);
	buildst();
	totsiz=n;
	getcog(1,0);
	buildvpt(cog);
	int lastans=0;
	while(m--){
		int opt=read();
		if(opt==0){
			int x=(read()^lastans),y=(read()^lastans);
			lastans=getans(x,y);
			printf("%d
",lastans);
		}
		else{
			int x=(read()^lastans),y=(read()^lastans);
			modi(x,y);
		}
	}
	return 0;
}

[BZOJ4372]烁烁的游戏

传送门

分析

怕和上一道题是一对。

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cctype>
#include <algorithm>
#define rin(i,a,b) for(int i=(a);i<=(b);i++)
#define rec(i,a,b) for(int i=(a);i>=(b);i--)
#define trav(i,x) for(int i=head[(x)];i;i=e[i].nxt)
typedef long long LL;
using std::cin;
using std::cout;
using std::endl;

inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}

const int MAXN=100005;
int n,m,ecnt,head[MAXN];
int dep[MAXN],id[MAXN],num[MAXN],pos[MAXN],st[25][MAXN<<1],tot,len;
int root,cog,totsiz,fa[MAXN],siz[MAXN];
int root1[MAXN],root2[MAXN],lc[MAXN*200],rc[MAXN*200],sum[MAXN*200],tag[MAXN*200];
int loc,ql,qr,kk,tot2;
bool vis[MAXN];
struct Edge{
	int to,nxt;
}e[MAXN<<1];

inline void add_edge(int bg,int ed){
	ecnt++;
	e[ecnt].to=ed;
	e[ecnt].nxt=head[bg];
	head[bg]=ecnt;
}

void dfs(int x,int pre,int depth){
	dep[x]=depth;
	id[x]=++tot;
	num[tot]=x;
	st[0][++len]=id[x];
	pos[x]=len;
	trav(i,x){
		int ver=e[i].to;
		if(ver==pre) continue;
		dfs(ver,x,depth+1);
		st[0][++len]=id[x];
	}
}

inline void buildst(){
	int lim=log2(len);
	rin(i,1,lim) rin(j,1,len-(1<<i)+1)
		st[i][j]=std::min(st[i-1][j],st[i-1][j+(1<<(i-1))]);
}

inline int lca(int x,int y){
	int xx=pos[x],yy=pos[y];
	if(xx>yy) std::swap(xx,yy);
	int lim=log2(yy-xx+1);
	return num[std::min(st[lim][xx],st[lim][yy-(1<<lim)+1])];
}

inline int getdis(int x,int y){
	return dep[x]+dep[y]-(dep[lca(x,y)]<<1);
}

void getcog(int x,int pre){
	siz[x]=1;
	bool flag=1;
	trav(i,x){
		int ver=e[i].to;
		if(ver==pre||vis[ver]) continue;
		getcog(ver,x);
		siz[x]+=siz[ver];
		if(siz[ver]>totsiz/2) flag=0;
	}
	if(totsiz-siz[x]>totsiz/2) flag=0;
	if(flag) cog=x;
}

void getsiz(int x,int pre){
	siz[x]=1;
	trav(i,x){
		int ver=e[i].to;
		if(ver==pre||vis[ver]) continue;
		getsiz(ver,x);
		siz[x]+=siz[ver];
	}
}

void buildvpt(int x){
	vis[x]=1;
	getsiz(x,0);
	trav(i,x){
		int ver=e[i].to;
		if(vis[ver]) continue;
		totsiz=siz[ver];
		getcog(ver,x);
		fa[cog]=x;
		buildvpt(cog);
	}
}

#define mid ((l+r)>>1)
int upd(int pre,int l,int r){
	int o=pre;
	if(!o) o=++tot;
	sum[o]+=(std::min(qr,r)-std::max(ql,l)+1)*kk;
	if(ql<=l&&r<=qr){
		tag[o]+=kk;
		return o;
	}
	if(mid>=ql) lc[o]=upd(lc[pre],l,mid);
	if(mid<qr) rc[o]=upd(rc[pre],mid+1,r);
	return o;
}

int query(int o,int l,int r){
	if(l==r) return sum[o];
	if(loc<=mid) return query(lc[o],l,mid)+tag[o];
	else return query(rc[o],mid+1,r)+tag[o];
}
#undef mid

inline int getans(int x){
	int now=x,ret=0;
	while(1){
		loc=getdis(x,now)+1;
		ret+=query(root1[now],1,n);
		if(!fa[now]) return ret;
		loc=getdis(x,fa[now])+1;
		ret-=query(root2[now],1,n);
		now=fa[now];
	}
}

inline void modi(int x,int y,int z){
	int now=x;
	while(1){
		int curdis=getdis(x,now);
		if(curdis<=y){
			ql=1,qr=y-curdis+1,kk=z;
			root1[now]=upd(root1[now],1,n);
		}
		if(!fa[now]) return;
		curdis=getdis(x,fa[now]);
		if(curdis<=y){
			ql=1,qr=y-curdis+1,kk=z;
			root2[now]=upd(root2[now],1,n);
		}
		now=fa[now];
	}
}

int main(){
	n=read(),m=read();
	rin(i,2,n){
		int u=read(),v=read();
		add_edge(u,v);
		add_edge(v,u);
	}
	dfs(1,0,1);
	buildst();
	totsiz=n;
	getcog(1,0);
	root=cog;
	buildvpt(cog);
	while(m--){
		char opt=getchar();
		while(!isalpha(opt)) opt=getchar();
		if(opt=='Q'){
			int x=read();
			printf("%d
",getans(x));
		}
		else{
			int x=read(),y=read(),z=read();
			modi(x,y,z);
		}
	}
	return 0;
}

[BZOJ3924][ZJOI2015]幻想乡战略游戏

传送门

分析

对点分树的每个结点维护(3)个值,(sum1[x],sum2[x],sum3[x]),分别表示(x)担当区域对(x)的贡献,对(fa[x])的贡献,以及权值和。

我们可以从点分树的根结点出发,根据当前结点的答案和其点分树上子结点的答案,来推出答案在当前结点还是在其在点分树上的某个子结点的担当区域内,不断递归下去即可得到答案。(这里结点(x)答案指的是(sum val[y] imes dist(x,y)))。

如何求某个结点的答案?还是爬点分树就可以了,在每一层:

[ans+=sum1[x]-sum2[x]+(sum3[fa[x]]-sum3[x])*dist(fa[x],AskedVertex) ]

代码

BZOJ Rank Last代码,欢迎各位dalao嘲讽。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cctype>
#include <algorithm>
#include <vector>
#define rin(i,a,b) for(int i=(a);i<=(b);i++)
#define rec(i,a,b) for(int i=(a);i>=(b);i--)
#define trav(i,a) for(int i=head[(a)];i;i=e[i].nxt)
typedef long long LL;
using std::cin;
using std::cout;
using std::endl;

inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}

const int MAXN=100005; 
int n,q,ecnt,head[MAXN];
int id[MAXN],num[MAXN],pos[MAXN],st[25][MAXN<<1],tot,len;
int root,cog,totsiz,siz[MAXN],fa[MAXN];
LL dep[MAXN],sum1[MAXN],sum2[MAXN],sum3[MAXN];
bool vis[MAXN];
std::vector<int> vec[MAXN],vec2[MAXN];
struct Edge{
	int to,nxt;
	LL w;
}e[MAXN<<1];

inline void add_edge(int bg,int ed,LL val){
	ecnt++;
	e[ecnt].to=ed;
	e[ecnt].nxt=head[bg];
	e[ecnt].w=val;
	head[bg]=ecnt;
}

void dfs(int x,int pre,LL depth){
	dep[x]=depth;
	id[x]=++tot;
	num[tot]=x;
	st[0][++len]=id[x];
	pos[x]=len;
	trav(i,x){
		int ver=e[i].to;
		if(ver==pre) continue;
		dfs(ver,x,depth+e[i].w);
		st[0][++len]=id[x];
	}
}

inline void buildst(){
	int lim=log2(len);
	rin(i,1,lim) rin(j,1,len-(1<<i)+1)
		st[i][j]=std::min(st[i-1][j],st[i-1][j+(1<<(i-1))]);
}

inline int lca(int x,int y){
	int xx=pos[x],yy=pos[y];
	if(xx>yy) std::swap(xx,yy);
	int lim=log2(yy-xx+1);
	return num[std::min(st[lim][xx],st[lim][yy-(1<<lim)+1])];
}

inline LL getdis(int x,int y){
	return dep[x]+dep[y]-(dep[lca(x,y)]<<1);
}

void getcog(int x,int pre){
	siz[x]=1;
	bool flag=1;
	trav(i,x){
		int ver=e[i].to;
		if(ver==pre||vis[ver]) continue;
		getcog(ver,x);
		siz[x]+=siz[ver];
		if(siz[ver]>totsiz/2) flag=0;
	}
	if(totsiz-siz[x]>totsiz/2) flag=0;
	if(flag) cog=x;
}

void getsiz(int x,int pre){
	siz[x]=1; 
	trav(i,x){
		int ver=e[i].to;
		if(ver==pre||vis[ver]) continue;
		getsiz(ver,x);
		siz[x]+=siz[ver];
	}
}

void buildvpt(int x){
	vis[x]=1;
	getsiz(x,0);
	trav(i,x){
		int ver=e[i].to;
		if(vis[ver]) continue;
		totsiz=siz[ver];
		getcog(ver,x);
		fa[cog]=x;
		vec[x].push_back(cog);
		vec2[x].push_back(ver);
		buildvpt(cog);
	}
}

inline void upd(int x,LL y){
	int now=x;
	while(1){
		sum1[now]+=y*getdis(x,now);
		sum3[now]+=y;
		if(!fa[now]) return;
		sum2[now]+=y*getdis(x,fa[now]);
		now=fa[now];
	}
}

inline LL getv(int x){
	int now=x;LL ret=0;
	while(1){
		ret+=sum1[now];
		if(!fa[now]) return ret;
		ret+=(sum3[fa[now]]-sum3[now])*getdis(x,fa[now]);
		ret-=sum2[now];
		now=fa[now];
	}
}

int query(int x){
	LL now=getv(x);
	rin(i,0,(int)vec[x].size()-1){
		int ver=vec2[x][i];
		if(getv(ver)<now) return query(vec[x][i]);
	}
	return x;
}

int main(){
	n=read(),q=read();
	rin(i,2,n){
		int u=read(),v=read();LL w=read();
		add_edge(u,v,w);
		add_edge(v,u,w);
	}
	dfs(1,0,0);
	buildst();
	totsiz=n;
	getcog(1,0);
	root=cog;
	buildvpt(cog);
	while(q--){
		int x=read();LL y=read();
		upd(x,y);
		printf("%lld
",getv(query(root)));
	}
	return 0;
}

[BZOJ1095][ZJOI2007]Hide 捉迷藏

还没做。

原文地址:https://www.cnblogs.com/ErkkiErkko/p/10171285.html