题目描述

小L 最近沉迷于塞尔达传说:荒野之息(The Legend of Zelda: Breath of The Wild)无法自拔,他尤其喜欢游戏中的迷你挑战。

游戏中有一个叫做“LCT” 的挑战,它的规则是这样子的:现在有一个N 个点的 树(Tree),每条边有一个整数边权vi ,若vi >= 0,表示走这条边会获得vi 的收益;若vi < 0 ,则表示走这条边需要支付- vi 的过路费。小L 需要控制主角Link 切掉(Cut)树上的 恰好K 条边,然后再连接 K 条边权为 0 的边,得到一棵新的树。接着,他会选择树上的两个点p; q ,并沿着树上连接这两点的简单路径从p 走到q ,并为经过的每条边支付过路费/ 获取相应收益。

海拉鲁大陆之神TemporaryDO 想考验一下Link。他告诉Link,如果Link 能切掉 合适的边、选择合适的路径从而使 总收益 - 总过路费最大化的话,就把传说中的大师之剑送给他。

小 L 想得到大师之剑,于是他找到了你来帮忙,请你告诉他,Link 能得到的 总收益 - 总过路费最大是多少。

题解

先考虑断掉k条边又连上k条边这个问题,因为最后我们只需要选取最终的树上的一条链,所以我们连上这k条边相当于是把这条链分成k+1段。

所以我们需要找到树上相互无交的严格的k+1条链(根据题目描述,一个点也可以算做一条链)。

为什么要严格k+1段呢?因为这k+1段之间不能有交,所以必然会空出k条没有被选的边,正好可以留给我们断边用。

好了,题目大概分析要这里,然后dp链的问题有一个经典做法就是背包,因为不能有交,所以每个点只可能有三种状态012,表示这个点向下的边中有几条被选了,dp时讨论一下。

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 300002
#define M 103
using namespace std;
typedef long long ll;
ll dp[N][M][3];
int tot,head[N],n,k;
inline ll rd(){
    ll x=0;char c=getchar();bool f=0;
    while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f?-x:x; 
} 
struct edge{int n,to;ll l;}e[N<<1];
inline void add(int u,int v,ll l){e[++tot].n=head[u];e[tot].to=v;head[u]=tot;e[tot].l=l;}
inline ll mx(ll x,ll y,ll z){
    if(x>y&&x>z)return x;
    return y>z?y:z;
}
void dfs(int u,int fa){
    dp[u][0][0]=dp[u][1][1]=0;
    for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){
        int v=e[i].to;
        dfs(v,u);
        for(int j=k+1;j>=0;--j){ 
        dp[u][j][2]=max(dp[u][j][2],dp[u][j][1]+dp[v][1][1]+e[i].l);
          for(int l=0;l<j;++l){
            ll w=mx(dp[v][j-l][0],dp[v][j-l][1],dp[v][j-l][2]);
            dp[u][j][0]=max(dp[u][j][0],dp[u][l][0]+w);
            dp[u][j][1]=max(max(dp[u][j][1],dp[u][l][0]+dp[v][j-l][1]+e[i].l),dp[u][l][1]+w);;
            dp[u][j][2]=max(dp[u][l][2]+w,max(dp[u][j][2],dp[u][l][1]+dp[v][j-l+1][1]+e[i].l));
        }
        }
    }
}
int main(){
    n=rd();k=rd();int u,v;ll w;
    for(int i=1;i<=n;++i)for(int j=0;j<=k+1;++j)for(int l=0;l<3;++l)dp[i][j][l]=-1e16;
    for(int i=1;i<n;++i){
        u=rd();v=rd();w=rd();
        add(u,v,w);add(v,u,w);
    }
    dfs(1,0);
    cout<<max(dp[1][k+1][2],max(dp[1][k+1][0],dp[1][k+1][1]));
    return 0;
}
View Code

相关文章:

  • 2022-12-23
  • 2021-07-20
  • 2021-06-06
  • 2021-11-09
  • 2021-11-22
  • 2021-12-31
  • 2021-08-21
  • 2021-07-30
猜你喜欢
  • 2022-12-23
  • 2021-11-13
  • 2022-12-23
  • 2022-12-23
  • 2021-10-08
  • 2021-10-27
相关资源
相似解决方案