传送门
题解:
考试时打的一个只考虑在根节点放一个传送门,每走到叶节点就返回根节点的情况。
首先如果我们不考虑是用传送门,则走完整棵树相当于一个DFS,每条边经过两次。那么我们先令答案等于二倍所有权值和,然后再减去使用传送门获得的收益(这些边只用经过一次)
我们设f[i][0/1]表示在/不在i点设置传送门的单边权值之和,越大越好
第一个dp转移为f[i][1]=max(f[i][1],f[to][1]+w[i]) 表示如果i点没有传送门,那么可能在祖先有传送门,则可以从i点的某个儿子传送回去。
第二个dp转移为f[i][0]= Sigma max(dp[to][0],dp[to][1]+w[i]) 表示在i点设置了传送门,那么在i的儿子设置传送门不会影响i点,考虑在i点设置或不设置的方案总和,若不设置则可以使用传送门返回。
最后答案减去最大的收益,即在考虑根节点设置或不设置传送门的情况的最大值。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#include<climits>
#define MAXA 2000005
using namespace std;
typedef long long LL;
int head[MAXA],cnt;
struct Rx_Graph {
int next,to,w;
}edge[MAXA];
void Add(int u,int v,int w) {
edge[++cnt].next = head[u];
edge[cnt].to = v;
edge[cnt].w = w;
head[u] = cnt;
}
LL n,x,y,z,Ans,f[MAXA][2];
void DP(int Now,int Last) {
for(int i=head[Now];i;i=edge[i].next)
if(edge[i].to != Last)
DP(edge[i].to,Now);
for(int i=head[Now];i;i=edge[i].next) {
int j = edge[i].to;
if(j == Last) continue;
f[Now][0] = max(f[Now][0],f[j][0] + edge[i].w);
f[Now][1] += max(f[j][0] + edge[i].w,f[j][1]);
}
}
int main() {
// freopen("portal.in","r",stdin);
// freopen("portal.out","w",stdout);
scanf("%lld",&n);
for(int i=1;i<n;i++) {
scanf("%lld %lld %lld",&x,&y,&z);
Add(x,y,z);
Add(y,x,z);
Ans += 2 * z;
}
DP(1,0);
printf("%lld",Ans - max(f[1][1],f[1][0]));
}