bzoj 3677

显然dp嘛

首先我们发现,蓝线的连接方式是有限的,具体的,对于每一个节点,其实只有两种可能的连线方式:

第一种:该节点是新来的节点,两个子节点是初始红线的两侧

第二种:该节点是新来的节点,一个子节点和该节点的父节点是红线的两侧

但是,初始是有一个节点的,因此我们考虑进行树形dp时以这个点为根

如果我们确定了这一点之后,我们就会发现第一种连线的方法是不可能的!

因此我们只需考虑第二种转移即可

考虑转移:一个点可以从两个方向转移过来:

如果这个点不是蓝线的中点,那么转移有:

$f[x][0]=sum max(f[to][0],f[to][1]+edge[i].val)$

如果这个点是蓝线的中点,那么转移有:

$f[x][1]=f[x][0]+max(f[to][0]+edge[i].val-max(f[to][0],f[to][1]+edge[i].val)$

这个转移看着很显然,但是问题在于我们起初是随便指定一个点为根的,他不一定合法,我们还需要换根!

如果每次换根都这样dp,时间复杂度就炸飞了

因此我们考虑能否快速维护换根之后的dp值:

首先发现,$f[x][0]$只与每个子节点有关,他需要的是每个子节点的$max(f[to][0],f[to][1]+edge[i].val)$

这个比较好办

但是下面那个就不好办了,因为我们需要分最大值转移过来和非最大值转移过来来讨论

这就比较麻烦了

所以对于每个点,我们维护$f[to][0]+edge[i].val-max(f[to][0],f[to][1]+edge[i].val$这一坨东西的最大值和次大值,分类转移即可

然后我们合并一下状态:设状态$g(i)$表示$f(i)(0)$的状态,忽略掉$f(i)(1)$

因为$f(i)(1)$可以由$g(i)$和最大值直接算出,因此我们不记录$f(i)(1)$也是可以的

那么每次换根的时候,$g(x)$可以直接更新,然后用最大值与次大值分类,更新$g(to)$,然后再更新节点$to$的最大值和次大值

这样就结束了

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
using namespace std;
int f[200005][3];
int g[200005];
int n;
struct Edge
{
    int nxt;
    int to;
    int val;
}edge[400005];
int head[200005];
int cnt=1;
int ans=0;
void add(int l,int r,int w)
{
    edge[cnt].nxt=head[l];
    edge[cnt].to=r;
    edge[cnt].val=w;
    head[l]=cnt++;
}
void dfs(int x,int fx)
{
    f[x][1]=f[x][2]=-0x3f3f3f3f;
    for(int i=head[x];i;i=edge[i].nxt)
    {
        int to=edge[i].to;
        if(to==fx)continue;
        dfs(to,x);
        int v=g[to]+edge[i].val-max(g[to],g[to]+f[to][1]+edge[i].val);
        if(v>f[x][1])f[x][2]=f[x][1],f[x][1]=v;
        else if(v>f[x][2])f[x][2]=v;
        g[x]+=max(g[to],g[to]+f[to][1]+edge[i].val);
    }
}
void redfs(int x,int fx)
{
    ans=max(ans,g[x]);
    for(int i=head[x];i;i=edge[i].nxt)
    {
        int to=edge[i].to;
        if(to==fx)continue;
        int w2=g[to],o1=f[to][1],o2=f[to][2];
        int gg=g[x]-max(g[to],g[to]+f[to][1]+edge[i].val);
        int gf;
        if(f[x][1]==g[to]+edge[i].val-max(g[to],g[to]+f[to][1]+edge[i].val))gf=f[x][2];
        else gf=f[x][1];
        g[to]+=max(gg,gg+gf+edge[i].val);
        gf=gg+edge[i].val-max(gg,gg+gf+edge[i].val);
        if(gf>f[to][1])f[to][2]=f[to][1],f[to][1]=gf;
        else if(gf>f[to][2])f[to][2]=gf;
        redfs(to,x);
        f[to][1]=o1,f[to][2]=o2;
        g[to]=w2;
    }
}
inline int read()
{
    int f=1,x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int main()
{
    n=read();
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read(),z=read();
        add(x,y,z),add(y,x,z);
    }
    dfs(1,1),redfs(1,1);
    printf("%d
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/zhangleo/p/11147491.html