BZOJ3730: 震波【动态点分治】

题目描述:

在一片土地上有N个城市,通过N-1条无向边互相连接,形成一棵树的结构,相邻两个城市的距离为1,其中第i个城市的价值为value[i]。
接下来你需要在线处理M次操作:
0 x k 表示发生了一次地震,震中城市为x,影响范围为k,所有与x距离不超过k的城市都将受到影响,该次地震造成的经济损失为所有受影响城市的价值和。
1 x y 表示第x个城市的价值变成了y。
为了体现程序的在线性,操作中的x、y、k都需要异或你程序上一次的输出来解密,如果之前没有输出,则默认上一次的输出为0。

1<=N,M<=100000,1<=value[i],y<=10000

题目分析:

动态点分治,在每个点分中心开一个树状数组维护子树中距离对应的权值和,另开一个树状数组维护子树中对点分树上父亲的贡献。(树状数组用指针开内存)
没怎么写动态点分治,有几个需要注意的地方:

  • O(1)求LCA的ST表的大小要开 2 n 2n 2n,倍增的循环边界也是 2 n 2n 2n
  • 由于要在当前点分中心的儿子上存它对自己的贡献,而当前并不知道子树内的重心是哪个点,所以要开一个临时数组存下对应子树需要维护的信息,再进入子树中,最后返回子树重心后将临时数组的值赋给它。
  • 注意爬父亲的时候求的始终是一开始的 x x x到当前点分中心的距离,而不是两个相邻点分中心的距离。
  • x x x与当前点分中心的距离如果超过了 k k k x x x与更上面的点分中心的距离并不一定超过 k k k

Code:

#include<bits/stdc++.h>
#define maxn 100005
using namespace std;
char cb[1<<18],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<18,stdin),cs==ct)?0:*cs++)
inline void read(int &a){
	char c;while(!isdigit(c=getc()));
	for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
const int Log = 17;
int n,m,w[maxn],dep[maxn],dfn[maxn],st[maxn<<1][Log+1],lg[maxn<<1],cnt;
int D[maxn],S[maxn],tp,siz[maxn],fa[maxn],ans;
bool vis[maxn];
int fir[maxn],nxt[maxn<<1],to[maxn<<1],tot;
inline void line(int x,int y){nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y;}
int arr[maxn*40],*idx=arr;
struct BIT{
	int *a,len;
	void upd(int i,int v){for(;i<=len;i+=i&-i) a[i]+=v;}
	int qsum(int i){int s=0;for(i=min(i,len);i;i-=i&-i) s+=a[i];return s;}
}A[maxn],B[maxn];
void dfs1(int u,int ff){
	st[dfn[u]=++cnt][0]=dep[u]=dep[ff]+1;
	for(int i=fir[u],v;i;i=nxt[i]) if((v=to[i])!=ff)
		dfs1(v,u),st[++cnt][0]=dep[u];
}
inline int Dist(int x,int y){
	int ret=dep[x]+dep[y];
	if((x=dfn[x])>(y=dfn[y])) swap(x,y);
	int k=lg[y-x+1];
	return ret-2*min(st[x][k],st[y-(1<<k)+1][k]);
}
void getroot(int u,int ff,int tsz,int &g){
	siz[u]=1; bool flg=1;
	for(int i=fir[u],v;i;i=nxt[i]) if(!vis[v=to[i]]&&v!=ff)
		getroot(v,u,tsz,g),siz[u]+=siz[v],flg&=siz[v]<<1<=tsz;
	if(flg&&(tsz-siz[u])<<1<=tsz) g=u;
}
void dfs2(int u,int ff,int &mxd){
	mxd=max(mxd,D[u]=D[ff]+1),S[++tp]=u;
	for(int i=fir[u],v;i;i=nxt[i]) if(!vis[v=to[i]]&&v!=ff) dfs2(v,u,mxd);
}
int TDC(int u,int tsz){
	getroot(u,0,tsz,u),vis[u]=1;
	tp=0,dfs2(u,0,A[u].len),A[u].a=idx,idx+=A[u].len+1;
	for(int i=1;i<=tp;i++) A[u].upd(D[S[i]],w[S[i]]);//D+1, qry's d+1.
	BIT tmp;//!!!
	for(int i=fir[u],v,x;i;i=nxt[i]) if(!vis[v=to[i]]){
		tp=tmp.len=0,dfs2(v,u,tmp.len),tmp.a=idx,idx+=tmp.len+1;
		for(int j=1;j<=tp;j++) tmp.upd(D[S[j]],w[S[j]]);
		x=TDC(v,siz[v]<siz[u]?siz[v]:tsz-siz[u]);
		fa[x]=u,B[x]=tmp;
	}
	return u;
}
void modify(int x,int v){
	int delta=v-w[x]; w[x]=v;
	A[x].upd(1,delta);
	for(int i=x,d;fa[i];i=fa[i])//attention to the d.
		d=Dist(fa[i],x),A[fa[i]].upd(d+1,delta),B[i].upd(d+1,delta);
}
void solve(int x,int k){
	ans=A[x].qsum(k+1);
	for(int i=x,d;fa[i];i=fa[i]){
		if((d=Dist(fa[i],x))>k) continue;//!!! not return!!!
		ans+=A[fa[i]].qsum(k-d+1)-B[i].qsum(k-d+1);
	}
}
int main()
{
	int x,y,op;
	read(n),read(m);
	for(int i=1;i<=n;i++) read(w[i]);
	for(int i=1;i<n;i++) read(x),read(y),line(x,y),line(y,x);
	dfs1(1,0),lg[0]=-1;
	for(int i=1;i<=cnt;i++) lg[i]=lg[i>>1]+1;
	for(int j=1;j<=Log;j++) for(int i=1;i+(1<<j)-1<=cnt;i++) st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
	TDC(1,n);
	while(m--){
		read(op),read(x),read(y),x^=ans,y^=ans;
		if(!op) solve(x,y),printf("%d\n",ans);
		else modify(x,y);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值