題目連接:https://ac.nowcoder.com/acm/contest/5672/Cios
題目大意:ui
一棵無根樹,每一個點都有一個點權 f(x)
,其初值均爲 0,有三種操做。操做1:對全部的 y,修改f(y) 爲 w - dist(x,y)
操做2:修改 f(x) 爲 min(f(x),0)
操做3:查詢 f(x)
spa
想法:code
對於操做一: w - dist(x,y) = w - dep[x] - dep[y] + 2 * dep[lca] 對於 dep[x] 和 dep[y] 咱們能夠直接獲得,因此如今問題轉化爲如何維護 2 * dep[lca]blog
因此對於操做一以後對於當前的 x ,咱們的答案就是 ans = sum【表明全部的w的和】 + qchain(x,1)【1->x的修改次數,即cnt】 - dep[x] * op1 【op1 表明進行了幾回操做一】- dist 【全部的dep[y]的和】- op2[x] 【操做二取最小值致使的差值】get
由於操做二的要麼是f[x] 要麼是 0,咱們不妨就維護一個最大值,每次變化的最終結果就是以前的值減去的最大的那個string
其他的基本上就是樹鏈剖分的板子了it
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <vector> #define ll long long #define ull unsigned long long #define ls nod<<1 #define rs (nod<<1)+1 #define pii pair<int,int> #define mp make_pair #define pb push_back #define INF 0x3f3f3f3f #define max(a, b) (a>b?a:b) #define min(a, b) (a<b?a:b) const double eps = 1e-8; const int maxn = 2e5 + 10; const ll MOD = 1e9 + 7; const int mlog=20; int sgn(double a) { return a < -eps ? -1 : a < eps ? 0 : 1; } using namespace std; int n,m; int head[maxn],cnt; struct Graph { int to,nxt; }edge[maxn]; void add_edge(int u,int v) { edge[cnt].to = v; edge[cnt].nxt = head[u]; head[u] = cnt++; edge[cnt].to = u; edge[cnt].nxt = head[v]; head[v] = cnt++; } int v[maxn]; // 結點權值 int fa[maxn]; // 父親 int dep[maxn]; // 深度 int siz[maxn]; // 大小 int son[maxn]; // 重兒子 // 第一遍的dfs能夠獲得深度,父親,大小,重兒子 void dfs1(int u,int f){ // u是當前結點,f是u的父親 fa[u] = f; dep[u] = dep[f] + 1; siz[u] = 1; int maxsize = -1; // 判斷是否是重兒子的一個臨時變量 for (int i=head[u];~i;i=edge[i].nxt){ int v = edge[i].to; if (v == f) //若是是父親確定不能夠 continue; dfs1(v,u); siz[u] += siz[v]; if (siz[v] > maxsize){ maxsize = siz[v]; son[u] = v; // u的重兒子就是v } } } int tim; // 時間戳計數器 int dfn[maxn]; // 時間戳 int top[maxn]; //重鏈的頂部 int w[maxn]; // 結點權值dfs序 void dfs2(int u,int t){ // u是當前結點,t是當前結點的重鏈的頭 dfn[u] = ++tim; top[u] = t; w[tim] = v[u]; if (!son[u]) // 若是不存在重兒子,那麼它就是葉子結點,退出 return; dfs2(son[u],t); for (int i=head[u];~i;i=edge[i].nxt){ int v = edge[i].to; if (v == fa[u] || v == son[u]) // 往上遍歷確定不能夠了,由於前面的dfs2已經遍歷了重兒子因此這裏也不必了 continue; dfs2(v,v); // 此時這個確定是輕兒子 } } struct segment_tree{ int l,r; ll val,lazy; }tree[maxn*4]; void pushup(int nod){ tree[nod].val = (tree[nod<<1].val + tree[(nod<<1)+1].val); } void pushdown(int nod){ tree[nod<<1].lazy += tree[nod].lazy; tree[(nod<<1)+1].lazy += tree[nod].lazy; tree[nod<<1].val += (tree[nod<<1].r-tree[nod<<1].l + 1) * tree[nod].lazy ; tree[(nod<<1)+1].val += (tree[(nod<<1)+1].r-tree[(nod<<1)+1].l+1) * tree[nod].lazy ; tree[nod].lazy = 0; } void build(int l,int r,int nod=1){ tree[nod].l = l; tree[nod].r = r; tree[nod].lazy = 0; tree[nod].val = 0; if (l == r){ tree[nod].lazy = 0; tree[nod].val = 0; return ; } int mid = (l+r)>>1; build(l,mid,nod<<1); build(mid+1,r,(nod<<1)+1); pushup(nod); } void modify(int x,int y,int z,int k=1){ int l = tree[k].l, r = tree[k].r; if (x<= l && y>=r){ tree[k].lazy += z; tree[k].val += (r-l+1) * z; return ; } if (tree[k].lazy) pushdown(k); int mid = (l+r)>>1; if (x<=mid){ modify(x,y,z,k<<1); } if (y>mid){ modify(x,y,z,(k<<1)+1); } pushup(k); } ll query(int x,int y,int k=1){ int l = tree[k].l,r = tree[k].r; if (x<=l && y>=r){ return tree[k].val; } if (tree[k].lazy){ pushdown(k); } ll sum = 0; int mid = (l+r)>>1; if (x <= mid){ sum += query(x,y,k<<1); } if (y > mid){ sum += query(x,y,(k<<1)+1); } return sum; } void mchain(int x,int y,int z){ // 結點x->結點y 最短路徑上全部結點加z while (top[x] != top[y]){ if (dep[top[x]] < dep[top[y]]) swap(x,y); modify(dfn[top[x]],dfn[x],z); x = fa[top[x]]; } if (dep[x] > dep[y]) swap(x,y); modify(dfn[x],dfn[y],z); } ll qchain(int x,int y){ // 查詢x到y結點最短路徑上全部節點的值之和 ll ret = 0; while (top[x] != top[y]){ if (dep[top[x]] < dep[top[y]]) swap(x,y); ret += query(dfn[top[x]],dfn[x]); x = fa[top[x]]; } if (dep[x] > dep[y]) swap(x,y); ret += query(dfn[x],dfn[y]); return ret ; } void mson(int x,int z){ // 以x爲根節點的子樹內全部節點值都加上z modify(dfn[x],dfn[x]+siz[x]-1,z); // 一定是連續的 } ll qson(int x){ // 以x爲根節點的子樹內全部節點值之和 return query(dfn[x],dfn[x]+siz[x]-1); } ll op2[maxn]; void init() { cnt = 0; tim = 0; memset(head,-1,sizeof(head)); memset(dfn,0,sizeof(dfn)); memset(top,0,sizeof(top)); memset(w,0,sizeof(w)); memset(v,0,sizeof(v)); memset(fa,0,sizeof(fa)); memset(dep,0,sizeof(dep)); memset(siz,0,sizeof(siz)); memset(son,0,sizeof(son)); memset(op2,0,sizeof(op2)); } int main() { int t; scanf("%d",&t); while (t--) { scanf("%d%d",&n,&m); init(); for (int i = 1;i < n;i++) { int u,v; scanf("%d%d",&u,&v); add_edge(u,v); } build(1,n); dfs1(1,0); dfs2(1,1); ll sum = 0,ans = 0,dist = 0,op1 = 0; for (int i = 1;i <= m;i++) { int op; scanf("%d",&op); if (op == 1) { op1++; int x,w; scanf("%d%d",&x,&w); sum += w; mchain(x,1,2); dist += dep[x]; } else if (op == 2) { int x; scanf("%d",&x); ans = sum + qchain(x,1) - dep[x] * op1 - dist - op2[x]; op2[x] += max(ans,0LL); } else if (op == 3) { int x; scanf("%d",&x); ans = sum + qchain(x,1) - dep[x] * op1 - dist - op2[x]; printf("%lld\n",ans); } } } return 0; }