BZOJ 3677: [Apio2014]连珠线 树形DP
倒在了性质分析上....(还有仔细读题)
仔细读题后,我们发现红色边只能连接一个连通块和一个叶节点(或两个叶节点).
然后如果一个状态是合法的,当且仅当以某个点为根时所有蓝边都是形如 son[x]->x->fa[x]
然后因为有这条性质,我们就可以进行树形DP了.
令 $1$ 为根,$f[x]$ 表示 $x$ 是蓝边中点时以 $x$ 为根的子树的最大价值,$g[x]$ 表示不是中点时的最大价值.
然后当根不是 $1$ 时,用换根DP的方式转移即可,需要记录一个最小/次小值跟着一起转移.
#include <cstdio> #include <string> #include <cstring> #include <algorithm> #define N 200007 #define inf 1000000000 using namespace std; void setIO(string s) { string in=s+".in"; string out=s+".out"; freopen(in.c_str(),"r",stdin); // freopen(out.c_str(),"w",stdout); } int edges,n,ans; int f[N],g[N],hd[N],to[N<<1],nex[N<<1],val[N<<1],f1[N],f2[N]; void add(int u,int v,int c) { nex[++edges]=hd[u],hd[u]=edges,to[edges]=v,val[edges]=c; } void dfs(int u,int ff) { g[u]=0,f[u]=-inf; f1[u]=f2[u]=-inf; for(int i=hd[u];i;i=nex[i]) { int v=to[i]; if(v==ff) continue; dfs(v,u); g[u]+=max(g[v],f[v]+val[i]); } for(int i=hd[u];i;i=nex[i]) { int v=to[i]; if(v==ff) continue; f[u]=max(f[u],g[u]-max(g[v],f[v]+val[i])+g[v]+val[i]); int tmp=g[v]+val[i]-max(g[v],f[v]+val[i]); if(tmp>f1[u]) f2[u]=f1[u],f1[u]=tmp; else if(tmp>f2[u]) f2[u]=tmp; } } void dfs2(int u,int ff) { ans=max(ans,g[u]); for(int i=hd[u];i;i=nex[i]) { int v=to[i]; if(v==ff) continue; int gu=g[u]-max(g[v],g[v]+f1[v]+val[i]),fu; if(f1[u]==g[v]+val[i]-max(g[v],g[v]+f1[v]+val[i])) fu=f2[u]; else fu=f1[u]; g[v]+=max(gu,gu+fu+val[i]); fu=gu+val[i]-max(gu,gu+fu+val[i]); if(fu>f1[v]) f2[v]=f1[v],f1[v]=fu; else if(fu>f2[v]) f2[v]=fu; dfs2(v,u); } } int main() { // setIO("input"); int i,j,x,y,z; scanf("%d",&n); for(i=1;i<n;++i) scanf("%d%d%d",&x,&y,&z),add(x,y,z),add(y,x,z); ans=-inf; dfs(1,0); dfs2(1,0); printf("%d\n",ans); return 0; }