codeforces 916E Jamie and Tree dfs序列化+线段树+LCA
2.5 seconds
256 megabytes
standard input
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:
1 v — Change the tree's root to vertex with number v.
2 u v x — For each vertex in the subtree of smallest size that contains u and v, add x to its value.
3 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!
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:
1 v (1 ≤ v ≤ n) for queries of the first type.
2 u v x (1 ≤ u, v ≤ n, - 108 ≤ x ≤ 108) for queries of the second type.
3 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.
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.
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
27
19
5
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
18
21
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的更多相关文章
- CodeForces 620E:New Year Tree(dfs序+线段树)
E. New Year Treetime limit per test3 secondsmemory limit per test256 megabytesinputstandard inputout ...
- codeforces 620E. New Year Tree dfs序+线段树+bitset
题目链接 给一棵树, 每个节点有颜色, 两种操作, 一种是将一个节点的子树全都染色成c, 一种是查询一个节点的子树有多少个不同的颜色, c<=60. 每个节点一个bitset维护就可以. #in ...
- POJ.3321 Apple Tree ( DFS序 线段树 单点更新 区间求和)
POJ.3321 Apple Tree ( DFS序 线段树 单点更新 区间求和) 题意分析 卡卡屋前有一株苹果树,每年秋天,树上长了许多苹果.卡卡很喜欢苹果.树上有N个节点,卡卡给他们编号1到N,根 ...
- 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 ...
- Codeforces 916E Jamie and Tree (换根讨论)
题目链接 Jamie and Tree 题意 给定一棵树,现在有下列操作: $1$.把当前的根换成$v$:$2$.找到最小的同时包含$u$和$v$的子树,然后把这棵子树里面的所有点的值加$x$: ...
- POJ3321 - Apple Tree DFS序 + 线段树或树状数组
Apple Tree:http://poj.org/problem?id=3321 题意: 告诉你一棵树,每棵树开始每个点上都有一个苹果,有两种操作,一种是计算以x为根的树上有几个苹果,一种是转换x这 ...
- BZOJ - 2588 Spoj 10628. Count on a tree (可持久化线段树+LCA/树链剖分)
题目链接 第一种方法,dfs序上建可持久化线段树,然后询问的时候把两点之间的所有树链扒出来做差. #include<bits/stdc++.h> using namespace std; ...
- 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 ...
- Codeforces 916E(思维+dfs序+线段树+LCA)
题面 传送门 题目大意:给定初始根节点为1的树,有3种操作 1.把根节点更换为r 2.将包含u,v的节点的最小子树(即lca(u,v)的子树)所有节点的值+x 3.查询v及其子树的值之和 分析 看到批 ...
随机推荐
- 控制台——EventLog实现事件日志操作
我们应该如何通过写代码的方式向其中添加“日志”呢? 在操作之前,先明确几个概念: 1:事件日志名(logName):“事件查看器”中的每一项,如“应用程序”.“Internet Explorer”.“ ...
- Gradle与Makefile构建工具的对比
随着Android Studio的普及,越来越多的Android开发者也要开始了解和学习Gradle这款强大的代码构建工具了.我们在学习和了解一项新事物的时候,最快速的方法往往是与已知的事物进行比较, ...
- Mysql 之show status数据详解
状态名 作用域 详细解释 Aborted_clients Global 由于客户端没有正确关闭连接导致客户端终止而中断的连接数 Aborted_connects Global 试图连接到MySQL服务 ...
- Windows系统设置与北京Internet时间同步
找到提供Ntp服务器的网址:http://support.ntp.org/我从中找到东北大学的: ntp.neu.edu.cn ( 202.118.1.46 ) 长话短说,下面开始设置: 修改后的值 ...
- /etc目录常用配置文件
/etc/resolv.conf DNS客户端配置文件,逐渐被网卡配置文件所替代 /etc/hosts 本机DNS解析文件,优先级高于DNS服务器 /etc/hostname CentOS 7 主机名 ...
- ubuntu14.04远程软件VNC的安装、设置方法
ubuntu中VNCX11開機啟動保持靜默狀態方法: 1首先安裝vncx11. 2.設置vnc密碼,會自動保存在/home/test/.vnc/passwd中 3.將密碼複製到/etc中 4.在/et ...
- 详解proxy_pass、upstream与resolver
详解proxy_pass.upstream与resolver boldcautious 关注 2018.06.04 10:48 字数 1204 阅读 1434评论 0喜欢 2 应用场景 这里列举几个应 ...
- C++中的各种进制转换函数汇总及学习
一.指定格式输出 1.C中指定格式输出 printf(); //按八进制格式输出,保留5位高位补零 printf(); //按十进制格式输出,保留3位高位补零 printf(); //按十六进制格式输 ...
- Delphi / Pascal 语法知识干货
********************************************* Pascal.Delph干货 *************************************** ...
- Sort HDU5884(二分+多叉哈夫曼树)
HDU5884 Sort 题意:有n个序列要进行归并,每次归并的代价是两个序列的长度的和,要求最终的代价不能超过规定的T,求在此前提下一次能同时进行归并的序列的个数k. 思路:还是太单纯,看完题目一直 ...