【BZOJ3730】震波 动态树分治+线段树

【BZOJ3730】震波

Description

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

Input

第一行包含两个正整数N和M。
第二行包含N个正整数,第i个数表示value[i]。
接下来N-1行,每行包含两个正整数u、v,表示u和v之间有一条无向边。
接下来M行,每行包含三个数,表示M次操作。

Output

包含若干行,对于每个询问输出一行一个正整数表示答案。

Sample Input

8 1
1 10 100 1000 10000 100000 1000000 10000000
1 2
1 3
2 4
2 5
3 6
3 7
3 8
0 3 1

Sample Output

11100101

HINT

1<=N,M<=100000
1<=u,v,x<=N
1<=value[i],y<=10000
0<=k<=N-1

题解:直接动态树分治,对于每一个分治中心,我们用维护一棵线段树维护它分治子树中的所有节点,下标为节点到分治中心的距离,权值为城市的权值。同时为了防重,还要维护一个从父亲那里减掉的版本。查询时,从当前点一直向父亲移动,每次在线段树中查一下就行了。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn=100010;
struct node
{
	int ls,rs,sum;
}s[maxn*200];
int n,m,cnt,tot,root,mn,ans;
int to[maxn<<1],next[maxn<<1],head[maxn],v[maxn],fa[maxn],siz[maxn],r1[maxn],r2[maxn],vis[maxn],Log[maxn<<1];
int md[20][maxn<<1],pos[maxn],dep[maxn];
void getrt(int x,int fa)
{
	siz[x]=1;
	int i,tmp=0;
	for(i=head[x];i!=-1;i=next[i])	if(!vis[to[i]]&&to[i]!=fa)
		getrt(to[i],x),siz[x]+=siz[to[i]],tmp=max(tmp,siz[to[i]]);
	tmp=max(tmp,tot-siz[x]);
	if(tmp<mn)	root=x,mn=tmp;
}
void solve(int x)
{
	vis[x]=1;
	for(int i=head[x];i!=-1;i=next[i])	if(!vis[to[i]])	tot=siz[to[i]],mn=1<<30,getrt(to[i],x),fa[root]=x,solve(root);
}
void dfs(int x)
{
	md[0][++pos[0]]=dep[x],pos[x]=pos[0];
	for(int i=head[x];i!=-1;i=next[i])	if(!dep[to[i]])	dep[to[i]]=dep[x]+1,dfs(to[i]),md[0][++pos[0]]=dep[x];
}
inline void add(int a,int b)
{
	to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
}
inline int getmin(int a,int b)
{
	if(a>b)	swap(a,b);
	int k=Log[b-a+1];
	return min(md[k][a],md[k][b-(1<<k)+1]);
}
inline int dis(int a,int b)
{
	return dep[a]+dep[b]-2*getmin(pos[a],pos[b]);
}
void updata(int l,int r,int &x,int a,int b)
{
	if(!x)	x=++tot;
	s[x].sum+=b;
	if(l==r)	return ;
	int mid=(l+r)>>1;
	if(a<=mid)	updata(l,mid,s[x].ls,a,b);
	else	updata(mid+1,r,s[x].rs,a,b);
}
int query(int l,int r,int x,int a,int b)
{
	if(!x||a>b)	return 0;
	if(a<=l&&r<=b)	return s[x].sum;
	int mid=(l+r)>>1;
	if(b<=mid)	return query(l,mid,s[x].ls,a,b);
	if(a>mid)	return query(mid+1,r,s[x].rs,a,b);
	return query(l,mid,s[x].ls,a,b)+query(mid+1,r,s[x].rs,a,b);
}
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int rd(){
    char ch=nc();int sum=0;
    while(!(ch>=‘0‘&&ch<=‘9‘))ch=nc();
    while(ch>=‘0‘&&ch<=‘9‘)sum=sum*10+ch-48,ch=nc();
    return sum;
}
int main()
{
	//freopen("bz3730.in","r",stdin);
	//freopen("bz3730.out","w",stdout);
	n=rd(),m=rd();
	int a,b,c;
	register int i,j,x,y;
	memset(head,-1,sizeof(head));
	for(i=1;i<=n;i++)	v[i]=rd();
	for(i=1;i<n;i++)	a=rd(),b=rd(),add(a,b),add(b,a);
	dep[1]=1,dfs(1),tot=n,mn=1<<30,getrt(1,0),solve(root);
	for(i=2;i<=pos[0];i++)	Log[i]=Log[i>>1]+1;
	for(j=1;(1<<j)<=pos[0];j++)	for(i=1;i+(1<<j)-1<=pos[0];i++)	md[j][i]=min(md[j-1][i],md[j-1][i+(1<<(j-1))]);
	tot=0;
	for(i=1;i<=n;i++)
	{
		x=i;
		while(x)
		{
			y=fa[x],updata(0,n,r1[x],dis(x,i),v[i]);
			if(y)	updata(0,n,r2[x],dis(y,i),v[i]);
			x=y;
		}
	}
	for(i=1;i<=m;i++)
	{
		c=rd(),a=rd()^ans,b=rd()^ans;
		if(!c)
		{
			ans=0,x=a;
			while(x)
			{
				y=fa[x],ans+=query(0,n,r1[x],0,b-dis(x,a));
				if(y)	ans-=query(0,n,r2[x],0,b-dis(y,a));
				x=y;
			}
			printf("%d\n",ans);
		}
		else
		{
			x=a;
			while(x)
			{
				y=fa[x],updata(0,n,r1[x],dis(x,a),b-v[a]);
				if(y)	updata(0,n,r2[x],dis(y,a),b-v[a]);
				x=y;
			}
			v[a]=b;
		}
	}
	return 0;
}
时间: 2024-10-05 06:17:02

【BZOJ3730】震波 动态树分治+线段树的相关文章

震波——动态点分治+线段树

题目 [题目描述] 在一片土地上有 $N$ 个城市,通过 $N-1$ 条无向边互相连接,形成一棵树的结构,相邻两个城市的距离为 $1$,其中第 $i$ 个城市的价值为 $value[i]$.不幸的是,这片土地常常发生地震,并且随着时代的发展,城市的价值也往往会发生变动. 接下来你需要在线处理 $M$ 次操作:- $0~x~k$ 表示发生了一次地震,震中城市为 $x$ ,影响范围为 $k$ ,所有与 $x$ 距离不超过 $k$ 的城市都将受到影响,该次地震造成的经济损失为所有受影响城市的价值和.-

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

[BZOJ4372]烁烁的游戏 Description 背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠.题意:给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠.烁烁他每次会跳到一个节点u,把周围与他距离不超过d的节点各吸引出w只皮皮鼠.皮皮鼠会被烁烁吸引,所以会一直待在节点上不动.烁烁很好奇,在当前时刻,节点u有多少个他的好朋友---皮皮鼠.大意:给一颗n个节点的树,边权均为1,初始点权均为0,m次操作:Q x:询问x的点权.M x d w:将树上与节点x距离不超过d的节点的点权均加上w. In

UVALive 7148 LRIP【树分治+线段树】

题意就是要求一棵树上的最长不下降序列,同时不下降序列的最小值与最大值不超过D. 做法是树分治+线段树,假设树根是x,y是其当前需要处理的子树,对于子树y,需要处理出两个数组MN,MX,MN[i]表示以x为第一个数字的不下降子序列中第i个数的最小值,MX[i]表示以x为第一个数字的不上升子序列中第i个数的最大值.如果当前子树有一个以x为首的不下降序列,那么我们就需要在之前处理的子树中找一条以x为首的满足约束条件不上升序列,可以用线段树来查询.同时每做完一颗子树的时候,用MN,MX对线段树进行更新.

【BZOJ-4636】蒟蒻的数列 动态开点线段树 ||(离散化) + 标记永久化

4636: 蒟蒻的数列 Time Limit: 30 Sec  Memory Limit: 256 MBSubmit: 247  Solved: 113[Submit][Status][Discuss] Description 蒟蒻DCrusher不仅喜欢玩扑克,还喜欢研究数列 题目描述 DCrusher有一个数列,初始值均为0,他进行N次操作,每次将数列[a,b)这个区间中所有比k小的数改为k,他想知 道N次操作后数列中所有元素的和.他还要玩其他游戏,所以这个问题留给你解决. Input 第一

Codeforces 803G Periodic RMQ Problem ST表+动态开节点线段树

思路: (我也不知道这是不是正解) ST表预处理出来原数列的两点之间的min 再搞一个动态开节点线段树 节点记录ans 和标记 lazy=-1 当前节点的ans可用  lazy=0 没被覆盖过 else 区间覆盖 push_up的时候要注意好多细节,, 数组尽量往大开 //By SiriusRen #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const in

【bzoj3939】[Usaco2015 Feb]Cow Hopscotch 动态开点线段树优化dp

题目描述 Just like humans enjoy playing the game of Hopscotch, Farmer John's cows have invented a variant of the game for themselves to play. Being played by clumsy animals weighing nearly a ton, Cow Hopscotch almost always ends in disaster, but this has

【bzoj4999】This Problem Is Too Simple! 树链剖分+动态开点线段树

题目描述 给您一颗树,每个节点有个初始值. 现在支持以下两种操作: 1. C i x(0<=x<2^31) 表示将i节点的值改为x. 2. Q i j x(0<=x<2^31) 表示询问i节点到j节点的路径上有多少个值为x的节点. 输入 第一行有两个整数N,Q(1 ≤N≤ 100,000:1 ≤Q≤ 200,000),分别表示节点个数和操作个数. 下面一行N个整数,表示初始时每个节点的初始值. 接下来N-1行,每行两个整数x,y,表示x节点与y节点之间有边直接相连(描述一颗树).

CF1045G AI robots(动态开点线段树)

题意 火星上有$N$个机器人排成一行,第$i$个机器人的位置为$x_{i}$,视野为$r_{i}$,智商为$q_{i}$.我们认为第$i$个机器人可以看到的位置是$[x_{i}-r_{i},x_{i}+r_{i}]$.如果一对机器人相互可以看到,且它们的智商$q_{i}$的差距不大于$K$,那么它们会开始聊天. 为了防止它们吵起来,请计算有多少对机器人可能会聊天. 题解 先膜一下大佬->这里 我们先按视野降序排序,这样一个一个考虑,如果后面的能看到前面,那前面的也肯定能看到后面 这样,就是对于每

CF915E Physical Education Lessons|动态开点线段树

动态开点线段树 题目暗示了区间修改,所以我们自然想到了用线段树来维护非工作日的天数. 然而我们再看一下数据范围,天数n的范围是\(1 \le n \le 10^9\),像普通线段树一样预处理显然会爆空间. 天无绝人之路,我们看一下修改个数,$1\le q \le 3 \cdot 10^5 $, 比天数少很多,这也意味着,我们预先处理好的线段树有些节点并没有用 能否优化呢?答案是肯定的,这就是动态开点线段树,顾名思义,我们只要到用某个节点的时候,才分配一个点给它,这样使得我们使用的空间大大减少.其