BZOJ4372: 烁烁的游戏【动态点分治+线段树】

9 篇文章 0 订阅
1 篇文章 0 订阅

4372: 烁烁的游戏

动态点分治,其实就是将点分树建出来,然后在树上做一些动态操作(不改变树的形态)。

对于每一个点,我们用线段树存下这个点的子树中所有原树上距离的权值。然后对于修改直接暴力跳父亲,容斥去重就可以了。询问也同样道理。

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

#include<cmath>
#include<cstdio>
#include<algorithm>
const int MAXN=1e5+5;
int n,m,Ans,tot,Fa[MAXN],TFa[MAXN][20],Dep[MAXN],All,Siz[MAXN],MaxSiz[MAXN],Root;bool vis[MAXN];
int T[2][MAXN],Tre[MAXN*120],Lt[MAXN*120],Rt[MAXN*120];
#include<cctype>
int read(){
	int ret=0;char ch=getchar();bool f=1;
	for(;!isdigit(ch);ch=getchar()) f^=!(ch^'-');
	for(; isdigit(ch);ch=getchar()) ret=(ret<<1)+(ret<<3)+ch-48;
	return f?ret:-ret;
}
struct Edge{
	int tot,lnk[MAXN],nxt[MAXN<<1],son[MAXN<<1];
	void Add(int x,int y){nxt[++tot]=lnk[x];lnk[x]=tot;son[tot]=y;}
}E;
void GetRoot(int x,int fa){
	Siz[x]=1;MaxSiz[x]=0;
	for(int j=E.lnk[x];j;j=E.nxt[j])
	if(E.son[j]!=fa&&!vis[E.son[j]]){
		GetRoot(E.son[j],x),Siz[x]+=Siz[E.son[j]];
		MaxSiz[x]=std::max(MaxSiz[x],Siz[E.son[j]]);
	}
	MaxSiz[x]=std::max(All-Siz[x],MaxSiz[x]);
	if(MaxSiz[x]<MaxSiz[Root]) Root=x;
}
void Build(int x,int fa){
	Fa[x]=fa;vis[x]=1;
	for(int j=E.lnk[x];j;j=E.nxt[j])
	if(E.son[j]!=fa&&!vis[E.son[j]]) All=Siz[E.son[j]],Root=0,GetRoot(E.son[j],x),Build(Root,x);
}
void DFS(int x,int fa){
	TFa[x][0]=fa;Dep[x]=Dep[fa]+1;
	for(int j=E.lnk[x];j;j=E.nxt[j]) if(E.son[j]!=fa) DFS(E.son[j],x);
}
void INIT(){
	for(int j=1;(1<<j)<=n;j++)
	for(int i=1;i<=n;i++) TFa[i][j]=TFa[TFa[i][j-1]][j-1];
}
int LCA(int p,int q){
	if(Dep[p]<Dep[q]) std::swap(p,q);
	int Del=Dep[p]-Dep[q];
	for(int j=0;(1<<j)<=Del;j++) if(Del&(1<<j)) p=TFa[p][j];
	if(p==q) return p;
	for(int j=log2(n);j>=0;j--)
	if(TFa[p][j]!=TFa[q][j]) p=TFa[p][j],q=TFa[q][j];
	return TFa[p][0];
}
int GetDist(int x,int y){return Dep[x]+Dep[y]-2*Dep[LCA(x,y)];}
void Insert(int &rt,int L,int R,int x,int p){
	if(!rt) rt=++tot;Tre[rt]+=p;
	if(L==R) return;int mid=(R+L)>>1;
	if(x<=mid) Insert(Lt[rt],L,mid,x,p);
	else Insert(Rt[rt],mid+1,R,x,p);
}
int Query(int rt,int L,int R,int l,int r){
	if(!rt) return 0;
	if(l<=L&&R<=r) return Tre[rt];
	int mid=(R+L)>>1,Sum=0;
	if(l<=mid) Sum+=Query(Lt[rt],L,mid,l,r);
	if(r>mid) Sum+=Query(Rt[rt],mid+1,R,l,r);
	return Sum;
}
void Updata(int rt,int x,int d,int w){
	int D=GetDist(rt,x);
	if(d-D>=0) Insert(T[0][rt],0,n,d-D,w);
	if(!Fa[rt]) return;
	D=GetDist(Fa[rt],x);
	if(d-D>=0) Insert(T[1][rt],0,n,d-D,w);
	Updata(Fa[rt],x,d,w);
}
void Ask(int rt,int x){
	Ans+=Query(T[0][rt],0,n,GetDist(rt,x),n);
	if(!Fa[rt]) return;
	Ans-=Query(T[1][rt],0,n,GetDist(Fa[rt],x),n);
	Ask(Fa[rt],x);
}
int main(){
//	freopen("4372.in","r",stdin);
//	freopen("4372.out","w",stdout);
	n=read(),m=read();
	for(int i=1,x,y;i<n;i++) x=read(),y=read(),E.Add(x,y),E.Add(y,x);
	DFS(1,0);INIT();All=n,MaxSiz[0]=1e9,Root=0,GetRoot(1,0),Build(Root,0);
	for(int i=1;i<=m;i++){
		char ch[10];scanf("%s",ch);
		if(ch[0]=='Q'){
			int x=read();Ans=0;Ask(x,x);
			printf("%d\n",Ans);
		}else{
			int x=read(),D=read(),w=read();
			Updata(x,x,D,w);
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值