E. Jamie and Tree
time limit per test

2.5 seconds

memory limit per test

256 megabytes

input

standard input

output

standard output

To your surprise, Jamie is the final boss! Ehehehe.

Jamie has given you a tree with n vertices, numbered from 1 to n. Initially, the root of the tree is the vertex with number 1. Also, each vertex has a value on it.

Jamie also gives you three types of queries on the tree:

v — Change the tree's root to vertex with number v.

u v x — For each vertex in the subtree of smallest size that contains u and v, add x to its value.

v — Find sum of values of vertices in the subtree of vertex with number v.

A subtree of vertex v is a set of vertices such that v lies on shortest path from this vertex to root of the tree. Pay attention that subtree of a vertex can change after changing the tree's root.

Show your strength in programming to Jamie by performing the queries accurately!

Input

The first line of input contains two space-separated integers n and q (1 ≤ n ≤ 105, 1 ≤ q ≤ 105) — the number of vertices in the tree and the number of queries to process respectively.

The second line contains n space-separated integers a1, a2, ..., an ( - 108 ≤ ai ≤ 108) — initial values of the vertices.

Next n - 1 lines contains two space-separated integers ui, vi (1 ≤ ui, vi ≤ n) describing edge between vertices ui and vi in the tree.

The following q lines describe the queries.

Each query has one of following formats depending on its type:

v (1 ≤ v ≤ n) for queries of the first type.

u v x (1 ≤ u, v ≤ n,  - 108 ≤ x ≤ 108) for queries of the second type.

v (1 ≤ v ≤ n) for queries of the third type.

All numbers in queries' descriptions are integers.

The queries must be carried out in the given order. It is guaranteed that the tree is valid.

Output

For each query of the third type, output the required answer. It is guaranteed that at least one query of the third type is given by Jamie.

Examples
input
6 7
1 4 2 8 5 7
1 2
3 1
4 3
4 5
3 6
3 1
2 4 6 3
3 4
1 6
2 2 4 -5
1 4
3 3
output
27
19
5
input
4 6
4 3 5 6
1 2
2 3
3 4
3 1
1 3
2 2 4 3
1 1
2 2 4 -3
3 1
output
18
21
Note

The following picture shows how the tree varies after the queries in the first sample.

大意:给出一颗树,初始根节点为1,三种操作:

1.把一个节点变成根节点

2.给出两个节点u,v,把包含这两个点的最小子树中每个节点权值加上x

3.查询以 u 为根的子树的权值和。

题解:

如果没有操作一,这题就是把LCA和dfs序列化以及线段树的板子放在一起。

但是,对于操作一,如何解决?

其实并不需要真的把节点提到根上。

我们可以一直以 1 为根节点,利用一些小结论来进行2和3操作。

假设在该图中,当前根节点为6,但是我们的dfs序列仍然是以1为根节点的。

如果当前根节点不在要修改(查询)的子树中,就没有任何影响,直接修改(查询)即可,此处不做讨论。

下面只说根节点在要修改(查询)的子树中的情况。

如果现在给出操作2,把包含2,4的最小子树上面的每个节点加delta。

我们应该处理的节点是 1 2 3 4 5

lca(2,4)=1     lca(2,6)=1    lca(4,6)=3

有一个大胆的猜想:

直接修改lca(2,4)=1的子树,然后把不该修改的哪部分改回去。

那么多修改的哪部分是那些呢:

大胆猜想:

取这三组lca中深度最大的那个(节点3)

当前的根节点6所在的     节点3的子树     就是多修改的那部分。

对于询问操作,思想大体相似

若当前根节点为6,询问1的子树的权值和。

先算上整颗树,然后减去多加的部分。

多加的部分就是根节点6所在的     节点1的子树,也就是3,4,5,6。

大胆猜想,不用证明,恩,这结论就是对的。

 /*
Welcome Hacking
Wish You High Rating
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
using namespace std;
int read(){
int xx=,ff=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')ff=-;ch=getchar();}
while(ch>=''&&ch<=''){xx=(xx<<)+(xx<<)+ch-'';ch=getchar();}
return xx*ff;
}
const int maxn=;
int N,Q,opt,t1,t2,t3,root,v[maxn];
int tid[maxn],rk[maxn],tim,Prev[maxn],depth[maxn],end[maxn];
int lin[maxn],len;
struct edge{
int y,next;
}e[maxn<<];
inline void insert(int xx,int yy){
e[++len].next=lin[xx];
lin[xx]=len;
e[len].y=yy;
}
void dfs(int x,int prev){
depth[x]=depth[prev]+;
tid[x]=++tim;
rk[tim]=x;
Prev[x]=prev;
for(int i=lin[x];i;i=e[i].next)
if(e[i].y!=prev)
dfs(e[i].y,x);
end[x]=tim;
}
int f[][maxn];
int LCA(int x,int y){
if(depth[x]<depth[y])
swap(x,y);
int dif=depth[x]-depth[y];
for(int i=;i>=;i--)
if(dif&(<<i))
x=f[i][x];
if(x==y)
return x;
for(int i=;i>=;i--)
if(f[i][x]!=f[i][y])
x=f[i][x],y=f[i][y];
return f[][x];
}
int LCALCA(int x,int y){
int t1=LCA(x,y),t2=LCA(x,root),t3=LCA(y,root);
if(depth[t1]<depth[t2])
swap(t1,t2);
if(depth[t1]<depth[t3])
return t3;
return t1;
}
int get_son(int x){
int temp=root;
int dif=depth[root]-depth[x]-;
for(int i=;i>=;i--)
if(dif&(<<i))
temp=f[i][temp];
return temp;
}
struct Segment_Tree{
long long sum,tag;
}T[maxn<<];
int X,Y,Z;
void build(int L,int R,int root){
if(L==R){
T[root].sum=v[rk[L]];
return;
}
int mid=(L+R)>>;
build(L,mid,root<<);
build(mid+,R,root<<|);
T[root].sum=T[root<<].sum+T[root<<|].sum;
}
void upd(int L,int R,int root){
if(X>R||Y<L)
return;
if(X<=L&&Y>=R){
T[root].tag+=Z;
T[root].sum+=1LL*(R-L+)*Z;
return;
}
int mid=(L+R)>>;
if(T[root].tag){
T[root<<].tag+=T[root].tag;
T[root<<|].tag+=T[root].tag;
T[root<<].sum+=T[root].tag*(mid-L+);
T[root<<|].sum+=T[root].tag*(R-mid);
T[root].tag=;
}
upd(L,mid,root<<);
upd(mid+,R,root<<|);
T[root].sum=T[root<<].sum+T[root<<|].sum;
}
long long query(int L,int R,int root){
if(X>R||Y<L)
return ;
if(X<=L&&Y>=R)
return T[root].sum;
int mid=(L+R)>>;
if(T[root].tag){
T[root<<].tag+=T[root].tag;
T[root<<|].tag+=T[root].tag;
T[root<<].sum+=T[root].tag*(mid-L+);
T[root<<|].sum+=T[root].tag*(R-mid);
T[root].tag=;
}
return query(L,mid,root<<)+query(mid+,R,root<<|);
}
int main(){
//freopen("in.txt","r",stdin);
N=read(),Q=read();root=;
for(int i=;i<=N;i++)
v[i]=read();
for(int i=;i<N;i++){
t1=read(),t2=read();
insert(t1,t2);
insert(t2,t1);
}
dfs(,);
/*for(int i=1;i<=N;i++)
printf("%d ",tid[i]);
puts("");
for(int i=1;i<=N;i++)
printf("%d ",rk[i]);
puts("");
for(int i=1;i<=N;i++)
printf("%d ",depth[i]);
puts("");*/
build(,N,);
for(int i=;i<=N;i++)
f[][i]=Prev[i];
for(int i=;i<=;i++)
for(int j=;j<=N;j++)
f[i][j]=f[i-][f[i-][j]];
while(Q--){
opt=read();
if(opt==)
root=read();
else if(opt==){
t1=read(),t2=read(),t3=read();
int lca=LCALCA(t1,t2);
if(tid[root]>=tid[lca]&&tid[root]<=end[lca]){
X=,Y=N,Z=t3;
upd(,N,);
if(lca==root)
continue;
int temp=get_son(lca);
X=tid[temp],Y=end[temp],Z=-t3;
upd(,N,);
}
else{
X=tid[lca],Y=end[lca],Z=t3;
upd(,N,);
}
}
else{
int lca=read();
if(lca==root){
X=,Y=N;
printf("%I64d\n",query(,N,));
}
else if(tid[root]>=tid[lca]&&tid[root]<=end[lca]){
long long ans=;
X=,Y=N;
ans=query(,N,);
int temp=get_son(lca);
X=tid[temp],Y=end[temp];
ans-=query(,N,);
printf("%I64d\n",ans);
}
else{
X=tid[lca],Y=end[lca];
printf("%I64d\n",query(,N,));
}
}
}
return ;
}

codeforces 916E Jamie and Tree dfs序列化+线段树+LCA的更多相关文章

  1. CodeForces 620E:New Year Tree(dfs序+线段树)

    E. New Year Treetime limit per test3 secondsmemory limit per test256 megabytesinputstandard inputout ...

  2. codeforces 620E. New Year Tree dfs序+线段树+bitset

    题目链接 给一棵树, 每个节点有颜色, 两种操作, 一种是将一个节点的子树全都染色成c, 一种是查询一个节点的子树有多少个不同的颜色, c<=60. 每个节点一个bitset维护就可以. #in ...

  3. POJ.3321 Apple Tree ( DFS序 线段树 单点更新 区间求和)

    POJ.3321 Apple Tree ( DFS序 线段树 单点更新 区间求和) 题意分析 卡卡屋前有一株苹果树,每年秋天,树上长了许多苹果.卡卡很喜欢苹果.树上有N个节点,卡卡给他们编号1到N,根 ...

  4. Codeforces Round #225 (Div. 2) E. Propagating tree dfs序+-线段树

    题目链接:点击传送 E. Propagating tree time limit per test 2 seconds memory limit per test 256 megabytes inpu ...

  5. Codeforces 916E Jamie and Tree (换根讨论)

    题目链接  Jamie and Tree 题意  给定一棵树,现在有下列操作: $1$.把当前的根换成$v$:$2$.找到最小的同时包含$u$和$v$的子树,然后把这棵子树里面的所有点的值加$x$: ...

  6. POJ3321 - Apple Tree DFS序 + 线段树或树状数组

    Apple Tree:http://poj.org/problem?id=3321 题意: 告诉你一棵树,每棵树开始每个点上都有一个苹果,有两种操作,一种是计算以x为根的树上有几个苹果,一种是转换x这 ...

  7. BZOJ - 2588 Spoj 10628. Count on a tree (可持久化线段树+LCA/树链剖分)

    题目链接 第一种方法,dfs序上建可持久化线段树,然后询问的时候把两点之间的所有树链扒出来做差. #include<bits/stdc++.h> using namespace std; ...

  8. CodeForces 916E Jamie and Tree(树链剖分+LCA)

    To your surprise, Jamie is the final boss! Ehehehe. Jamie has given you a tree with n vertices, numb ...

  9. Codeforces 916E(思维+dfs序+线段树+LCA)

    题面 传送门 题目大意:给定初始根节点为1的树,有3种操作 1.把根节点更换为r 2.将包含u,v的节点的最小子树(即lca(u,v)的子树)所有节点的值+x 3.查询v及其子树的值之和 分析 看到批 ...

随机推荐

  1. html5——多媒体(二)

    基本方法 load() //重新加载视频 play() //播放 pause() //暂停 基本属性 currentTime //视频播放的当前进度. duration //视频的总时间 paused ...

  2. JS——for

    打印两行星星: <script> for (var i = 0; i < 2; i++) { for (var j = 0; j < 10; j++) { document.w ...

  3. vs for Mac中的启用Entity Framework Core .NET命令行工具

    在vs for Mac的工具菜单中已没有了Package Manager Console. 我们可以通过以下方法使用Entity Framework Core .NET命令行工具: 1.添加Nuget ...

  4. 记录--git命令行上传项目到github仓库

    由于公司一直使用的是的SVN,基本上都是内网,原来的git命令都快忘记了,当然也是自己太懒,平时都是直接拖到github上.今天打开idea后突然看到了原来自己写好的一个项目,就想将它上传到githu ...

  5. 一个ROS的服务,使机器人向前移动指定距离

    源代码有点长,放文末链接里了. 服务描述及代码现在的服务是:请求时携带要前进的距离,然后底盘前进相应距离.代码如下,改动很小: #!/usr/bin/env python import rospyfr ...

  6. react 子组件调用父组件方法

    import React from 'react'import '../page1/header.css'import { Table } from 'antd'import Child from ' ...

  7. Django工程

    一.Django工程创建 1.Django安装: pip3 install django 安装成功后,会在python的安装目录下“Scripts"中生成”django-admin.exe& ...

  8. codevs 1160 蛇形矩阵

    1160 蛇形矩阵 传送门  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 白银 Silver 题解       题目描述 Description 小明玩一个数字游戏,取个n行 ...

  9. Delphi / Pascal 语法知识干货

    ********************************************* Pascal.Delph干货 *************************************** ...

  10. JS练习:切换图片

    代码: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title ...