[CF916E]Jamie and Tree——樹鏈剖分+線段樹
阿新 • • 發佈:2019-02-19
題目大意:
有一棵n個點的樹,每個節點上有一個權值wi,最開始根為1號點.現在有3種
型別的操作:
• 1 root, 表示將根設為root.
• 2 u v x, 設u, v的最近公共祖先為p, 將p的子樹中的所有點的權值加上x.
• 3 u, 查詢u的子樹中的所有點的權值和.
對於每個3操作,輸出答案.
思路:
如果真的換根的話肯定是不行的。。。所以我們要想辦法在原樹上面搞出不同的根的結果。
至於如何修改和查詢一棵樹的子樹的權值和,我們可以發現當在原樹中根不在點的子樹中的時候,答案就是子樹,如果在的話就用全體的減去下面那一部分就好了。
我們還要解決的一個問題就是如何在根節點變化的情況下求lca,找一找規律
然後就樹鏈剖分+線段樹就好了。
/*========================================
* Author : ylsoi
* Problem : CF916E
* Algrithm : Segment_Tree & tree%^$%*^
* Time : 2018.6.20
* ====================================*/
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;
using namespace std;
void File(){
freopen("CF916E.in","r",stdin);
freopen("CF916E.out","w",stdout);
}
template<typename T>void read(T &x){
T _=0,mul=1;
char ch=getchar();
while(!isdigit(ch)){if(ch=='-')mul=-1;ch=getchar();}
while(isdigit(ch))_=(_<<1 )+(_<<3)+(ch^'0'),ch=getchar();
x=_*mul;
}
template<typename T>void write(T x,char ch){
if(!x){putchar('0'); putchar(ch); return; }
if(x<0){putchar('-'),x*=-1;}
ll y=10;
int len=1;
while(y<=x)y=(y<<1)+(y<<3),++len;
while(len--){
y=y/10;
putchar(x/y+48);
x=x%y;
}
putchar(ch);
}
const int maxn=3e5+10;
struct Segment_Tree{
#define mid ((l+r)>>1)
#define lc (rt<<1)
#define rc (rt<<1|1)
#define lson lc,l,mid
#define rson rc,mid+1,r
ll sum[maxn<<2],tag[maxn<<2];
void pushdown(int rt,int l,int r){
sum[lc]+=tag[rt]*(mid-l+1);tag[lc]+=tag[rt];
sum[rc]+=tag[rt]*(r-mid); tag[rc]+=tag[rt];
tag[rt]=0;
}
void update(int rt,int l,int r,int L,int R,ll x){
if(L<=l && r<=R){
sum[rt]+=(r-l+1)*x;
tag[rt]+=x;
return;
}
if(tag[rt])pushdown(rt,l,r);
if(L<=mid)update(lson,L,R,x);
if(R>=mid+1)update(rson,L,R,x);
sum[rt]=sum[lc]+sum[rc];
}
ll query(int rt,int l,int r,int L,int R){
if(L<=l && r<=R)return sum[rt];
ll ret=0ll;
if(tag[rt])pushdown(rt,l,r);
if(L<=mid)ret+=query(lson,L,R);
if(R>=mid+1)ret+=query(rson,L,R);
return ret;
}
}T;
int n,q,root,w[maxn],cnt;
int to[maxn<<1],last[maxn<<1],beg[maxn],cnte;
int fa[maxn],son[maxn],top[maxn],siz[maxn],dep[maxn],cnt_dfn,dfn[maxn];
void add(int u,int v){
last[++cnte]=beg[u];
beg[u]=cnte;
to[cnte]=v;
}
void dfs1(int u,int f){
fa[u]=f;
siz[u]=1;
dep[u]=dep[f]+1;
for(int i=beg[u];i;i=last[i]){
if(to[i]==f)continue;
dfs1(to[i],u);
siz[u]+=siz[to[i]];
if(siz[to[i]]>siz[son[u]])
son[u]=to[i];
}
}
void dfs2(int u,int t){
dfn[u]=++cnt_dfn;
top[u]=t;
if(son[u])dfs2(son[u],t);
for(int i=beg[u];i;i=last[i]){
if(to[i]==fa[u] || to[i]==son[u])continue;
dfs2(to[i],to[i]);
}
}
int find(int u,int v){
while(top[u]!=top[v]){
++cnt;
if(dep[top[u]]<dep[top[v]])swap(u,v);
u=fa[top[u]];
}
return dep[u]<dep[v] ? u : v;
}
int change(int u){
int v=root;
while(top[v]!=top[u]){
if(fa[top[v]]==u)return top[v];
v=fa[top[v]];
}
return son[u];
}
void init(){
read(n); read(q);
root=1;
REP(i,1,n)read(w[i]);
REP(i,1,n-1){
int u,v;
read(u); read(v);
add(u,v);
add(v,u);
}
dfs1(1,0);
dfs2(1,1);
REP(i,1,n)T.update(1,1,n,dfn[i],dfn[i],w[i]);
}
int main(){
File();
init();
REP(i,1,q){
int ty,u,v;
ll x;
read(ty);
if(ty==1){
read(u);
root=u;
}
else if(ty==2){
read(u); read(v); read(x);
int lca=find(u,v),tmp;
if(dep[tmp=find(u,root)]>dep[lca])lca=tmp;
if(dep[tmp=find(v,root)]>dep[lca])lca=tmp;
if(lca==root)T.update(1,1,n,1,n,x);
else if(dfn[root]<dfn[lca] || dfn[root]>dfn[lca]+siz[lca]-1)
T.update(1,1,n,dfn[lca],dfn[lca]+siz[lca]-1,x);
else{
lca=change(lca);
T.update(1,1,n,1,n,x);
T.update(1,1,n,dfn[lca],dfn[lca]+siz[lca]-1,-x);
}
}
else{
ll sum=0ll;
read(u);
if(u==root)sum=T.query(1,1,n,1,n);
else if(dfn[root]<dfn[u] || dfn[root]>dfn[u]+siz[u]-1)
sum=T.query(1,1,n,dfn[u],dfn[u]+siz[u]-1);
else{
u=change(u);
sum+=T.query(1,1,n,1,n);
sum-=T.query(1,1,n,dfn[u],dfn[u]+siz[u]-1);
}
write(sum,'\n');
}
}
return 0;
}