动态 DP
有一个显然的
观察到这个 DP 是
矩阵乘法具有优良的性质:它满足结合律!
所以直接使用树剖来维护矩乘的信息,然后就做完了!
本题代码
#include<bits/stdc++.h> #define end xiaoheige #define int long long using namespace std; const int maxn = 200005, inf = 1e18; int n, m; struct edge {int y, nxt;} e[maxn * 2]; int hd[maxn], num; inline void join(int x, int y){e[++num] = (edge){y, hd[x]}; hd[x] = num;} struct mat{ int ele[2][2]; mat(int type = 0) { for(int i=0;i<2;i++) for(int j=0;j<2;j++) ele[i][j] = inf; if(type == 1) for(int i=0;i<2;i++) ele[i][i] = 0; } int& operator()(const int ix, const int iy){return ele[ix][iy];} inline friend mat operator*(mat mx, mat my) { mat res(0); for(int i=0;i<2;i++) for(int k=0;k<2;k++) for(int j=0;j<2;j++) res(i,j)=min(res(i,j), mx(i,k)+my(k,j)); return res; } }; int val[maxn], f[maxn]; mat g[maxn]; int dep[maxn], pa[maxn], siz[maxn], son[maxn], top[maxn], dfn[maxn], rev[maxn], end[maxn], cnt = 0; void dfs1(int u, int fa) { siz[u] = 1; dep[u] = dep[fa] + 1; pa[u] = fa; for(int i=hd[u];i;i=e[i].nxt) { int v = e[i].y; if(v == fa) continue; dfs1(v, u); siz[u] += siz[v]; if(siz[son[u]] < siz[v]) son[u] = v; } } void dfs2(int u, int t) { dfn[u] = ++cnt; rev[cnt] = u; top[u] = t; end[t] = max(end[t], cnt); g[u](0, 0) = 0; g[u](0, 1) = val[u]; g[u](1, 0) = inf; g[u](1, 1) = 0; f[u] = 0; if(son[u]) {dfs2(son[u], t); f[u] += f[son[u]];} else g[u](0, 0) = inf; for(int i=hd[u];i;i=e[i].nxt) { int v = e[i].y; if(v == pa[u] || v == son[u]) continue; dfs2(v, v); g[u](0, 0) += f[v]; f[u] += f[v]; } if(!son[u]) f[u] = val[u]; else f[u] = min(f[u], val[u]); } struct jiji{int l, r; mat val;}t[maxn<<2]; inline void pushUp(int k){t[k].val = t[k<<1].val*t[k<<1|1].val;} void build(int l, int r, int k){ t[k].l = l; t[k].r = r; if(l == r) {t[k].val = g[rev[l]]; return ;} int mid = t[k].l+t[k].r>>1; build(l, mid, k<<1); build(mid+1, r, k<<1|1); pushUp(k); } void update(int x, int k){ if(t[k].l == t[k].r) {t[k].val = g[rev[x]]; return;} int mid = t[k].l+t[k].r>>1; if(x <= mid) update(x, k<<1); else update(x, k<<1|1); pushUp(k); } mat query(int x, int y, int k) { if(t[k].l == x && t[k].r == y) return t[k].val; int mid = t[k].l+t[k].r>>1; if(y <= mid) return query(x, y, k<<1); else if(x >= mid+1) return query(x, y, k<<1|1); else return query(x, mid, k<<1)*query(mid+1, y, k<<1|1); } inline void updatePath(int x, int z) { val[x] += z; g[x](0, 1) = val[x]; mat bef, aft; while(x) { bef = query(dfn[top[x]], end[top[x]], 1); update(dfn[x], 1); aft = query(dfn[top[x]], end[top[x]], 1); x = pa[top[x]]; g[x](0, 0) += min(aft(0, 0), aft(0, 1)) - min(bef(0, 0), bef(0, 1)); } } inline int querySubtree(int x) { mat res = query(dfn[x], end[x], 1); return min(res(0, 0), res(0, 1)); } signed main() { ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin >> n; for(int i=1;i<=n;++i) cin >> val[i]; for(int i=1;i<n;i++) { int a, b; cin >> a >> b; join(a, b); join(b, a); } dfs1(1, 0); dfs2(1, 1); for(int i=1;i<=n;i++) end[i] = end[top[i]]; build(1, n, 1); cin >> m; while(m--) { char opt; int x; int z; cin>>opt; if(opt == 'C') {cin >> x >> z; updatePath(x, z);} else {cin >> x; printf("%lld\n",querySubtree(x));} } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步