【题目背景】
Tom在学写动态树,但是做题时过了样例,提交RE。Tom抓住Jerry要他写个暴力来对拍。Jerry觉得这任务太简单了,就让你来完成一下。
【题目描述】
有一棵n个节点的树,初始时根节点为1。现在要支持如下操作——1、将某节点设置为根;2、改变某节点权值;3、询问以某节点为根的子树内节点权值之和;4、询问以某两点 为端点的链上的节点权值之和。
【输入格式】
第一行两个正整数n和q,表示树的节点数和操作个数。
接着n-1行每行两个整数u和v,表示有连接u和v的一条边。
随后一行n个正整数,表示每个点的初始权值。
之后q行每行格式如下:
首先一个范围为1~4的正整数,表示该操作类型。
对于1操作,之后一个正整数x,表示将x节点设置为根。
对于2操作,之后两个正整数x和v,表示将x节点的权值改为v。
对于3操作,之后一个正整数x,表示询问以x为根的子树内节点权值之和。
对于4操作,之后两个正整数x和y,表示询问以x和y为端点的链上的节点权值之和。
【输出格式】
对于每个操作3和操作4,输出一行一个整数表示询问的答案。
【输入样例1】
3 3
1 2
2 3
3 2 1
3 1
1 2
3 1
【输出样例1】
6
3
【输入样例2】
1 3
1
4 1 1
2 1 2
4 1 1
【输出样例2】
1
2
题解:
裸的树链剖分,最后重新记录换了根对答案的影响
#include<bits/stdc++.h>
using namespace std;
int n,q;
int head[200005];
int next[200005];
int ver[200005];
int tot,cnt;
int size[100005];
int zson[100005];
int d[100005];
int id[100005];
int fa[100005];
int top[100005];
int val[100005];
int nowval[100005];
int tree[400005];
int root,calc;
int read(){
int num=0;
char ch=getchar();
while(ch>'9'||ch<'0'){
ch=getchar();
}
while(ch>='0'&&ch<='9'){
num=(num<<1)+(num<<3)+ch-'0';
ch=getchar();
}
return num;
}
void add(int x,int y){
ver[++tot]=y;
next[tot]=head[x];
head[x]=tot;
}
void dfs1(int x,int fat,int deep){
size[x]=1;
fa[x]=fat;
d[x]=deep;
for(int i=head[x];i;i=next[i]){
if(ver[i]==fat) continue;
dfs1(ver[i],x,deep+1);
size[x]+=size[ver[i]];
if(size[ver[i]]>size[zson[x]]){
zson[x]=ver[i];
}
}
}
void dfs2(int x,int topp){
id[x]=++cnt;
nowval[cnt]=val[x];
top[x]=topp;
if(!zson[x]) return;
dfs2(zson[x],topp);
for(int i=head[x];i;i=next[i]){
if(ver[i]==zson[x] || ver[i]==fa[x])continue;
dfs2(ver[i],ver[i]);
}
}
void build(int node,int l,int r){
if(l==r){
tree[node]=nowval[l];
return;
}
int mid=(l+r)>>1;
build(node<<1,l,mid);
build((node<<1)|1,mid+1,r);
tree[node]=tree[node<<1]+tree[(node<<1)|1];
}
void update(int node,int l,int r,int goal,int v){
if(l==r){
calc=tree[node];
tree[node]=v;
return;
}
int mid=(l+r)>>1;
if(goal<=mid){
update(node<<1,l,mid,goal,v);
}
else update((node<<1)|1,mid+1,r,goal,v);
tree[node]=tree[node]-calc+v;
}
int check(int x,int y){
if(x==y)return -1;
if(d[x]>d[y])return 0;
while(d[x]<=d[y]){
if(fa[y]==x)
return y;
y=fa[y];
}
return 0;
}
void work(int now,int l,int r,int L,int R){
if(l>=L && r<=R){
calc+=tree[now];
return;
}
else{
int mid=(l+r)>>1;
if(L<=mid)
work(now<<1,l,mid,L,R);
if(R>mid)
work((now<<1)|1,mid+1,r,L,R);
}
}
int hhh(int x,int y){
int ans=0;
while(top[x]!=top[y]){
if(d[top[x]]<d[top[y]]) swap(x,y);
calc=0;
work(1,1,n,id[top[x]],id[x]);
ans+=calc;
x=fa[top[x]];
}
if(d[x]>d[y]) swap(x,y);
calc=0;
work(1,1,n,id[x],id[y]);
ans+=calc;
return ans;
}
int main(){
n=read(),q=read();
for(int i=1;i<n;i++){
int x=read(),y=read();
add(x,y);
add(y,x);
}
for(int i=1;i<=n;i++)
val[i]=read();
size[0]=-1;
dfs1(1,0,1);
dfs2(1,1);
build(1,1,n);
int k,x,v,y;
for(int i=1;i<=q;i++){
k=read();
if(k==1){
root=read();
continue;
}
if(k==2){
x=read(),v=read();
update(1,1,n,id[x],v);
continue;
}
if(k==3){
x=read();
int q=check(x,root);
if(q==0){
calc=0;
work(1,1,n,id[x],id[x]+size[x]-1);
printf("%d\n",calc);
continue;
}
if(q==-1){
calc=0;
work(1,1,n,id[1],id[1]+size[1]-1);
printf("%d\n",calc);
continue;
}
calc=0;
work(1,1,n,id[1],id[1]+size[1]-1);
int num1=calc;
calc=0;
work(1,1,n,id[q],id[q]+size[q]-1);
printf("%d\n",num1-calc);
continue;
}
if(k==4){
x=read();
y=read();
printf("%d\n",hhh(x,y));
}
}
}