九省联考2018 林克卡特树

林克卡特树

有一棵树,你要选出 \(k+1\) 条点不相交的链,使得它们的长度加起来最大。

\(n \leq 3\times 10^5\)

题解

DP状态设计:
https://blog.csdn.net/stone41123/article/details/84061375
代码实现:
https://www.cnblogs.com/zwfymqz/p/9163274.html

这题有个尽量多选还是尽量少选的问题。

如果写的是尽量多选的话,会被以下数据卡掉:

11 5
1 2 1
2 3 1
3 4 1
4 5 1
5 6 1
6 7 1
7 8 1
8 9 1
9 10 1
10 11 1

具体原因不清楚,因为带权二分的方案构造很困难。尽量少选就是对的。

考试的时候造个类似的数据两种都试一下就行了。实践是检验真理的唯一标准。

struct node {int x,y;}; // value,number
IN bool operator<(CO node&a,CO node&b){
    return a.x!=b.x?a.x to[N];
node dp[N][3];
int mid;

void dfs(int u,int fa){
    dp[u][2]=max(dp[u][2],{-mid,1});
    for(CO edge&e:to[u])if(e.v!=fa){
        dfs(e.v,u);
        dp[u][2]=max(dp[u][2]+dp[e.v][0],dp[u][1]+dp[e.v][1]+(node){e.w-mid,1});
        dp[u][1]=max(dp[u][1]+dp[e.v][0],dp[u][0]+dp[e.v][1]+(node){e.w});
        dp[u][0]=dp[u][0]+dp[e.v][0];
    }
    dp[u][0]=max(dp[u][0],max(dp[u][1]+(node){-mid,1},dp[u][2]));
}

signed main(){
    int n=read(),K=read()+1;
    for(int i=1;i(),v=read(),w=read();
        to[u].push_back({v,w}),to[v].push_back({u,w});
    }
    int l=-1e6,r=3e11;
    while(l>1;
        memset(dp+1,0,n*3*sizeof(node));
        dfs(1,0);
        if(dp[1][0].y>=K) l=mid;
        else r=mid-1;
    }
    mid=l;
    memset(dp+1,0,n*3*sizeof(node));
    dfs(1,0);
    printf("%lld\n",dp[1][0].x+mid*K);
    return 0;
}

你可能感兴趣的:(九省联考2018 林克卡特树)