A National Pandemic (思维 + 树链剖分模版)
题目链接:https://ac.nowcoder.com/acm/contest/5672/C
题目大意:
一棵无根树,每个点都有一个点权 f(x)
,其初值均为 0,有三种操作。操作1:对所有的 y,修改f(y) 为 w - dist(x,y)
操作2:修改 f(x) 为 min(f(x),0)
操作3:查询 f(x)
想法:
对于操作一: w - dist(x,y) = w - dep[x] - dep[y] + 2 * dep[lca] 对于 dep[x] 和 dep[y] 我们可以直接得到,所以现在问题转化为如何维护 2 * dep[lca]
所以对于操作一之后对于当前的 x ,我们的答案就是 ans = sum【代表所有的w的和】 + qchain(x,1)【1->x的修改次数,即cnt】 - dep[x] * op1 【op1 代表进行了几次操作一】- dist 【所有的dep[y]的和】- op2[x] 【操作二取最小值导致的差值】
因为操作二的要么是f[x] 要么是 0,我们不妨就维护一个最大值,每次变化的最终结果就是之前的值减去的最大的那个
其余的基本上就是树链剖分的板子了
#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; }