luogu P3647 [APIO2014] 连珠线
https://www.luogu.com.cn/problem/P3647
我写DP像cxk
发现只有两种连边的方式
假设最优的策略已经给了出来,那么一定存在选定某个点为根的时候,只存在第一种脸边的情况
于是跑换根DP即可
具体的话设\(f[u][0/1]\)表示\(u\)是否作为中间点\(u\)往上延伸,的最大得分
转移还是挺显然的
\(f[u][0]=\sum\limits_{v\in son} \max(f[v][0],f[v][1]+w[v])\)
\(w[v]\)表示\(fa[u]->u\)的边权
\(f[u][1]=f[u][0]+\max(w[v]+f[v][0] - \max(f[v][0],f[v][1]+w[v]))\)
就是把上面\(v\)的贡献\(\max(f[v][0],f[v][1]+w[v])\)挖掉,然后加上\(v\)作为起点 往上延伸的贡献\(w[v]+f[v][0]\)
换根看起来不太好换
\(f[u][1]\)因为涉及到最大值,所以要记录\(u\)的 儿子中的最大值和次大值
考虑换根转移
设\(ls[u][0/1]\)表示图中圈出来的那蓝色部分,那\(ls[fa][0/1]\)表示的就是图中圈起来的绿色部分
因为\(f[u][1]\)转移维护最大值的时候没有维护到父亲的转移,所以要用\(ls[fa]\)来重新更新一下\(ls[u]\)
然后设\(g[u][0]\)表示以\(u\)为根的答案
然后按照上面说的转移即可
具体实现看代码吧
code:
#include<bits/stdc++.h>
#define N 400050
#define ll long long
using namespace std;
struct edge {
int v, nxt, c;
} e[N << 1];
int p[N], eid;
void init() {
memset(p, -1, sizeof p);
eid = 0;
}
void insert(int u, int v, int c) {
e[eid].v = v;
e[eid].c = c;
e[eid].nxt = p[u];
p[u] = eid ++;
}
const ll inf = 1e18;
ll ma1[N], ma2[N], f[N][2], g[N][2], ls[N][2], w[N];
int son1[N], son2[N], n;
void dfs(int u, int fa) {
ma1[u] = ma2[u] = - inf, son1[u] = son2[u] = 0;
for(int i = p[u]; i + 1; i = e[i].nxt) {
int v = e[i].v, c = e[i].c;
if(v == fa) continue; w[v] = c;
dfs(v, u);
int o = max(f[v][0], f[v][1] + w[v]);
f[u][0] += o;
if(f[v][0] + w[v] - o > ma1[u]) son2[u] = son1[u], ma2[u] = ma1[u], ma1[u] = f[v][0] + w[v] - o, son1[u] = v;
else if(f[v][0] + w[v] - o > ma2[u]) ma2[u] = f[v][0] + w[v] - o, son2[u] = v;
}
f[u][1] = f[u][0] + ma1[u];
}
void dfss(int u, int fa) {
for(int i = p[u]; i + 1; i = e[i].nxt) {
int v = e[i].v, c = e[i].c;
if(v == fa) continue;
if(son1[u] == v) swap(son1[u], son2[u]), swap(ma1[u], ma2[u]);
ls[u][0] = g[u][0] - max(f[v][0], f[v][1] + w[v]);
ls[u][1] = ls[u][0] + ma1[u];
if(fa) ls[u][1] = max(ls[u][1], ls[u][0] + ls[fa][0] + w[u] - max(ls[fa][0], ls[fa][1] + w[u]));
g[v][0] = f[v][0] + max(ls[u][0], ls[u][1] + w[v]);
if(ma1[u] < ma2[u]) swap(son1[u], son2[u]), swap(ma1[u], ma2[u]);
dfss(v, u);
}
}
int main() {
init();
scanf("%d", &n);
for(int i = 1; i < n; i ++) {
int u, v, c;
scanf("%d%d%d", &u, &v, &c);
insert(u, v, c), insert(v, u, c);
}
dfs(1, 0);
g[1][0] = f[1][0];
dfss(1, 0);
// for(int i = 1; i <= n; i ++) printf("%lld ", f[i][0]); printf("\n");
// for(int i = 1; i <= n; i ++) printf("%lld ", g[i][0]); printf("\n");
ll ans = 0;
for(int i = 1; i <= n; i ++) ans = max(ans, g[i][0]);
printf("%lld", ans);
return 0;
}