【洛谷】P2986 [USACO10MAR]Great Cow Gathering G【树上DP】【换根DP/找重心】
多亏了队里巨巨让我理解了第二种解法orzorz能遇到佬佬们的我真是太幸运辣!!
P2986 [USACO10MAR]Great Cow Gathering G
第一眼就觉得是找重心,可是发现权值是根据所选点动态变化的,然后懵了一会儿想到了第一种解法。
既然有点权和边权那就分开处理,首先用$siz[u]$处理出每个子树总的$c$值,再假设$1$号节点为所求,先统计一遍总不方便值$f[1]$,再跑一遍$dfs$,很容易发现此时往下$DP$时以$v$点为根的不方便值就应该是$f[v]=f[u]-siz[v]*w+(sum-siz[v])*w$,直接找出最优即可。
#include<bits/stdc++.h> #define ll long long const ll oo = 1e55; const int N = 100005; using namespace std; int n; ll c[N]; struct Node { int v, nex; ll w; Node(int v = 0, int nex = 0, ll w = 0) : v(v), nex(nex), w(w) { } } Edge[N << 1]; int stot, h[N]; void add(int u, int v, ll w) { Edge[++stot] = Node(v, h[u], w); h[u] = stot; } ll siz[N], d[N], f[N]; void dfs1(int u, int fa) { siz[u] = c[u]; for(int i = h[u]; i; i = Edge[i].nex) { int v = Edge[i].v; if(v == fa) continue; dfs1(v, u); siz[u] += siz[v]; f[u] += f[v]; f[u] += siz[v] * Edge[i].w; } } ll sum; void dfs2(int u, int fa) { for(int i = h[u]; i; i = Edge[i].nex) { int v = Edge[i].v; if(v == fa) continue; f[v] = f[u] + (sum - siz[v]) * Edge[i].w - siz[v] * Edge[i].w; dfs2(v, u); } } int main() { scanf("%d", &n); for(int i = 1; i <= n; i ++) { scanf("%d", &c[i]); sum += c[i]; } for(int i = 1; i < n; i ++) { int u, v; ll w; scanf("%d%d%lld", &u, &v, &w); add(u, v, w); add(v, u, w); } dfs1(1, 0); dfs2(1, 0); ll ans = oo; for(int i = 1; i <= n; i ++) ans = min(ans, f[i]); printf("%lld", ans); return 0; }
第二种方法找带权树重心然后换根DP。
发现了对于每一条连接$u$和$v$的边,取$u$和取$v$为根对答案贡献的区别就是是用$u$子树的$siz$乘这条边权还是用$v$的$siz$。画画图就发现只有当带权树重心为根时能保证每条边的贡献最小。这个权值就应该是$siz$。
于是跑出重心来直接用每个点到重心距离乘$c$即可。(注意这里不用$siz$,因为只用分别计当前点的贡献即可)
(强迫症又专门写了一遍自己的代码风格)
#include<bits/stdc++.h> #define ll long long const ll oo = 1e55; const int N = 100005; using namespace std; int n; ll c[N]; struct Node { int v, nex; ll w; Node(int v = 0, int nex = 0, ll w = 0) : v(v), nex(nex), w(w) { } } Edge[N << 1]; int stot, h[N]; void add(int u, int v, ll w) { Edge[++stot] = Node(v, h[u], w); h[u] = stot; } ll d[N], f[N], res = 0x3f3f3f3f, rt; int siz[N], sum; void dfs1(int u, int fa) { int maxr = 0; siz[u] = c[u]; for(int i = h[u]; i; i = Edge[i].nex) { int v = Edge[i].v; if(v == fa) continue; dfs1(v, u); siz[u] += siz[v]; maxr = max(maxr, siz[v]); } maxr = max(maxr, sum - siz[u]); if(maxr < res) res = maxr, rt = u; } ll dis[N]; void dfs2(int u, int fa) { for(int i = h[u]; i; i = Edge[i].nex) { int v = Edge[i].v; if(v == fa) continue; dis[v] = dis[u] + Edge[i].w; dfs2(v, u); } } int main() { scanf("%d", &n); for(int i = 1; i <= n; i ++) { scanf("%d", &c[i]); sum += c[i]; } for(int i = 1; i < n; i ++) { int u, v; ll w; scanf("%d%d%lld", &u, &v, &w); add(u, v, w); add(v, u, w); } dfs1(1, 0); dfs2(rt, 0); ll ans = 0; for(int i = 1; i <= n; i ++) ans += 1ll * c[i] * dis[i]; printf("%lld", ans); return 0; }
呱呱呱呱呱呱呱呱呱呱呱呱呱呱呱(?)