1. 程式人生 > >[CF916E]Jamie and Tree——樹鏈剖分+線段樹

[CF916E]Jamie and Tree——樹鏈剖分+線段樹

題目大意:

有一棵n個點的樹,每個節點上有一個權值wi,最開始根為1號點.現在有3種
型別的操作:
• 1 root, 表示將根設為root.
• 2 u v x, 設u, v的最近公共祖先為p, 將p的子樹中的所有點的權值加上x.
• 3 u, 查詢u的子樹中的所有點的權值和.
對於每個3操作,輸出答案.

思路:

如果真的換根的話肯定是不行的。。。所以我們要想辦法在原樹上面搞出不同的根的結果。
至於如何修改和查詢一棵樹的子樹的權值和,我們可以發現當在原樹中根不在點的子樹中的時候,答案就是子樹,如果在的話就用全體的減去下面那一部分就好了。
我們還要解決的一個問題就是如何在根節點變化的情況下求lca,找一找規律

可以發現,lca就是lca(u,v),lca(u,root),lca(v,root)中的深度最大的那個節點。
然後就樹鏈剖分+線段樹就好了。

/*========================================
 * 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; }