植物学家 (HGOI 2019.2.16 T4)

hicc0305

2019-02-16 16:21:06

Personal

## 题目大意 给出一棵有n个节点的树,每个点有自己的权值,一开始以r为根。 给出m次操作,每次给出x,y。x=1时代表把y旋转为根;x=2时代表,询问y的子树权值和为多少。 ## 数据范围 对于所有的数据,满足1<=a[i]<=100,1<=n,m<=100000,保证数据全部随机生成。 ### 解法 考试的时候听到大家在说什么dfs序啊,倍增啊,甚至还有树链剖分的。。。 根本全都不需要!!!只是一个很简单很简单的换根,可以说比第一题都简单。 时间复杂度为$O(n+m*k)$,k是什么那,做1操作时,时间复杂度是当前点的深度,2操作时是$O(1)$查询,在加上数据随机生成,所以不可能数据是一条链,反复从一头调到另一头这种极端数据,所以完全没问题! 先扫一遍数,把子树权值和、父亲是谁处理出来,然后换根的时候,影响的只有它的父节点,所以把父亲节点的父亲与子树和换一遍就好了。。。 ``` #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int n,m,cnt=-1,rt,tot=0; int val[100100],f[100100],sum[100100]; int head[200100],nxt[200100],to[200100]; void addedge(int x,int y) { cnt++; nxt[cnt]=head[x]; head[x]=cnt; to[cnt]=y; } void Calc(int u,int fa) { f[u]=fa;sum[u]=val[u]; for(int i=head[u];i!=-1;i=nxt[i]) { int v=to[i]; if(v==fa) continue; Calc(v,u); sum[u]+=sum[v]; } } void Change(int u) { if(!f[u]) return; Change(f[u]); sum[f[u]]-=sum[u]; sum[u]+=sum[f[u]]; f[f[u]]=u; } int main() { memset(head,-1,sizeof(head)); scanf("%d%d",&n,&m); for(int i=1;i<n;i++) { int x,y; scanf("%d%d",&x,&y); addedge(x,y); addedge(y,x); } for(int i=1;i<=n;i++) scanf("%d",&val[i]),tot+=val[i]; scanf("%d",&rt); Calc(rt,0); while(m--) { int x,y; scanf("%d%d",&x,&y); if(x==1) Change(y),f[y]=0; else printf("%d\n",sum[y]); } return 0; } ```