3730: 震波
Time Limit: 15 Sec Memory Limit: 256 MB
Submit: 2074 Solved: 430
[Submit][Status][Discuss]
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
sol::
点分树是个好东西啊,可以把树高变成log的。很多事情在点分树上搞都可以暴力搞然后把n变成log。
记得把点分树上一个点u对他的父亲v的影响消除掉,具体就是另外开一个线段树记录一下。注意一件事,你在跳点分树的时候要一直跳下去,因为点u可能在询问点的很远的地方,但是他的祖先可能就在询问点的旁边。
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
using namespace std;
int n,m;
inline int read()
{
char c;
int res,flag=0;
while((c=getchar())>'9'||c<'0') if(c=='-')flag=1;
res=c-'0';
while((c=getchar())>='0'&&c<='9') res=(res<<3)+(res<<1)+c-'0';
return flag?-res:res;
}
const int N=2e5+7;
const int M=1e5+7;
int nex[N],fir[M],go[N],tot;
int fa[M];
inline void add(int x,int y)
{
nex[++tot]=fir[x];fir[x]=tot;go[tot]=y;
}
namespace point
{
int ma,rt,sum;
int size[M];
bool vis[M];
inline void frt(int u,int f)
{
int e,v,jj=0;
size[u]=1;
for(e=fir[u];v=go[e],e;e=nex[e])
if(v!=f&&!vis[v])
{
frt(v,u);
size[u]+=size[v];
if(size[v]>jj) jj=size[v];
}
if(sum-size[u]>jj) jj=sum-size[u];
if(jj<ma) ma=jj,rt=u;
}
inline void build(int u)
{
vis[u]=1;
int e,v;
frt(u,u);
for(e=fir[u];v=go[e],e;e=nex[e])
if(!vis[v])
{
ma=1e9;
sum=size[v];
frt(v,u);
fa[rt]=u;
build(rt);
}
}
}
int rt[M][2],ans;
namespace tree
{
const int M=N*30;
int tot,size[M],lc[M],rc[M];
inline void modify(int &k,int l,int r,int x,int y)
{
if(!k) k=++tot;
size[k]+=y;
if(l==r) return;
int mid=l+r>>1;
if(mid>=x) modify(lc[k],l,mid,x,y);
else modify(rc[k],mid+1,r,x,y);
}
inline int query(int k,int l,int r,int x)
{
if(x<0) return 0;
if(x>=r) return size[k];
int mid=l+r>>1,res=0;
res+=query(lc[k],l,mid,x);
if(mid<x) res+=query(rc[k],mid+1,r,x);
return res;
}
}
namespace lca
{
const int maxlog=16;
int fa[M][maxlog+1];
int dep[M];
inline void init()
{
for(int j=1;j<=maxlog;++j)
for(int i=1;i<=n;++i)
fa[i][j]=fa[fa[i][j-1]][j-1];
}
inline int lca(int x,int y)
{
int ans=0;
if(dep[x]<dep[y]) swap(x,y);
for(int j=maxlog;j>=0;--j)
if(dep[x]-(1<<j)>=dep[y])
x=fa[x][j],ans+=(1<<j);
if(x==y) return ans;
for(int j=maxlog;j>=0;--j)
if(fa[x][j]!=fa[y][j])
x=fa[x][j],y=fa[y][j],ans+=(1<<j+1);
return ans+2;
}
inline void dfs(int u,int f)
{
int e,v;
for(e=fir[u];v=go[e],e;e=nex[e])
if(v!=f)
{
fa[v][0]=u;
dep[v]=dep[u]+1;
dfs(v,u);
}
}
inline void build()
{
dfs(1,1);
init();
}
}
inline void modify(int y,int v)
//u表示点分树上的一个点 ,y表示修改的权值,v表示修改的点
{
int u=v;
tree::modify(rt[u][0],0,n,0,y);
while(fa[u])
{
int dis=lca::lca(v,fa[u]);
tree::modify(rt[fa[u]][0],0,n,dis,y);
tree::modify(rt[u][1],0,n,dis,y);
u=fa[u];
}
}
inline void query(int v,int k)
{
int u=v;
ans=tree::query(rt[u][0],0,n,k);
while(fa[u])
{
int dis=k-lca::lca(fa[u],v);
ans+=tree::query(rt[fa[u]][0],0,n,dis);
ans-=tree::query(rt[u][1],0,n,dis);
u=fa[u];
}
}
int val[M];
int main()
{
// freopen("wave.in","r",stdin);
// freopen("wave.out","w",stdout);
n=read();
m=read();
for(int i=1;i<=n;++i) val[i]=read();
for(int i=2;i<=n;++i)
{
int x,y;
x=read();y=read();
add(x,y);add(y,x);
}
point::ma=point::sum=n;
point::frt(1,1);
point::build(point::rt);
lca::build();
for(int i=1;i<=n;++i)
modify(val[i],i);
while(m--)
{
int tp,x,y;
tp=read();
x=read()^ans;y=read()^ans;
if(tp) modify(y-val[x],x),val[x]=y;
else
{
ans=0;
query(x,y);
printf("%d\n",ans);
}
}
}