[八省联考2018]林克卡特树

题目描述

小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;
}

因为有负边权的存在,随着k的增加,答案不一定是严格单调递增的。

但是我们可以发现,答案是一个上凸函数 。

解决这种问题一般的操作是带权二分。

在这道题中,我们二分一个权mid,代表每选择一条链都要付出-mid的代价。

然后按照上面的方法直接按照权值大小贪心的做O(n)的dp。

然后看看最优解选没选够k+1条链,够了就加大mid,否则减小mid。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define N 300002
#define M 103
#define inf 1e18
using namespace std;
typedef long long ll;
int tot,head[N],n,k;
ll sum,mid,ans;
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;}
struct node{
    ll val,x;
    bool operator <(const node &b)const{
        return val==b.val?x>b.x:val<b.val;
    }
    node operator +(const node &b)const{
        return node{val+b.val,x+b.x};
    }
    node operator +(const ll &b)const{
        return node{val+b,x};
    }
}dp[N][3];
void dfs(int u,int fa){
    dp[u][2]=node{-mid,1};dp[u][0]=node{0,0};dp[u][1]=node{0,0};
    for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){
        int v=e[i].to;
        dfs(v,u);
        dp[u][2]=max(dp[u][2]+dp[v][0],dp[u][1]+dp[v][1]+node{e[i].l-mid,1});
        dp[u][1]=max(dp[u][1]+dp[v][0],dp[u][0]+dp[v][1]+e[i].l);
        dp[u][0]=dp[u][0]+dp[v][0];
    }
    dp[u][0]=max(dp[u][0],max(dp[u][1]+node{-mid,1},dp[u][2]));
}
int main(){
    n=rd();k=rd();int u,v;ll w;k++;
    for(int i=1;i<n;++i){
        u=rd();v=rd();w=rd();sum+=abs(w);
        add(u,v,w);add(v,u,w);
    }
    ll l=-sum,r=sum;
    while(l<=r){
        mid=(l+r)>>1;
        dfs(1,0);
        if(dp[1][0].x<=k)ans=mid,r=mid-1;else l=mid+1;
    }
    mid=ans;
    dfs(1,0);
    cout<<dp[1][0].val+ans*k;
    return 0;
}

原文地址:https://www.cnblogs.com/ZH-comld/p/10380860.html

时间: 2024-11-06 03:39:13

[八省联考2018]林克卡特树的相关文章

解题:八省联考2018 林克卡特树

题面 DP凸优化 题目并不难 先转化问题,显然k=0的时候我们都知道是求直径,然后k=1就是选两条点不相交的链拼起来,很容易推出题目就是要我们在树上选$k+1$条点不相交的链 事实上你直接按照边不相交做,取k+1次直径都可以得到50pts的好成绩,我佛了(不要问我怎么知道的 这个东西是可以DP的(稍微有点麻烦):设$dp[i][j][0/1/2]$表示以$i$为根的子树里选出j条链,这时i的度数为0/1/2的最优解.度数为零表示i是单独一个点,度数为1表示是链的端点,度数为2就是链中间的一个点

P4383 [八省联考2018]林克卡特树lct 树形DP+凸优化/带权二分

$ \color{#0066ff}{ 题目描述 }$ 小L 最近沉迷于塞尔达传说:荒野之息(The Legend of Zelda: Breath of The Wild)无法自拔,他尤其喜欢游戏中的迷你挑战. 游戏中有一个叫做"LCT" 的挑战,它的规则是这样子的:现在有一个N 个点的 树(Tree),每条边有一个整数边权vi ,若vi >= 0,表示走这条边会获得vi 的收益:若vi < 0 ,则表示走这条边需要支付- vi 的过路费.小L 需要控制主角Link 切掉(

[八省联考2018]林克卡特树lct

#include<bits/stdc++.h> #define RG register #define IL inline #define _ 500005 #define INF 1e18 #define ll long long using namespace std; IL ll gi(){ RG ll data = 0 , m = 1; RG char ch = 0; while(ch != '-' && (ch<'0' || ch > '9')) ch =

[九省联考2018]林克卡特树(DP+wqs二分)

对于k=0和k=1的点,可以直接求树的直径. 然后对于60分,有一个重要的转化:就是求在树中找出k+1条点不相交的链后的最大连续边权和. 这个DP就好.$O(nk^2)$ 然后我们完全不可以想到,将best[k](选择k条链的答案)打表输出,更不可能然后作差分,发现得到的数组是递减的. 这说明:best[k]是一个上凸包. 于是我们可以二分一个斜率去切这个凸包(类似导数),根据切点横坐标与k的大小旋转直线(改变斜率). 考虑给你一个直线斜率k,怎么找到它和凸包的切点.实际上就相当于将这个凸函数减

【BZOJ5251】【八省联考2018】劈配(网络流,二分答案)

[BZOJ5251][八省联考2018]劈配(网络流,二分答案) 题面 洛谷 BZOJ Description 一年一度的综艺节目<中国新代码>又开始了. Zayid从小就梦想成为一名程序员,他觉得这是一个展示自己的舞台,于是他毫不犹豫地报名了. 题目描述 轻车熟路的Zayid顺利地通过了海选,接下来的环节是导师盲选,这一阶段的规则是这样的: 总共n名参赛选手(编号从1至n)每人写出一份代码并介绍自己的梦想.接着由所有导师对这些选手进行排名. 为了避免后续的麻烦,规定不存在排名并列的情况. 同

[八省联考2018] 劈配

题目背景 一年一度的综艺节目<中国新代码>又开始了.Zayid 从小就梦想成为一名程序员,他觉得这是一个展示自己的舞台,于是他毫不犹豫地报名了. 题目描述 轻车熟路的Zayid 顺利地通过了海选,接下来的环节是导师盲选,这一阶段的规则是这样的: 总共n 名参赛选手(编号从1 至n)每人写出一份代码并介绍自己的梦想.接着 由所有导师对这些选手进行排名.为了避免后续的麻烦,规定不存在排名并列的情况. 同时,每名选手都将独立地填写一份志愿表,来对总共 m 位导师(编号从 1 至 m)作出评价.志愿表

洛谷P4382 [八省联考2018]劈配(网络流,二分答案)

洛谷题目传送门 说不定比官方sol里的某理论最优算法还优秀一点? Orz良心出题人,暴力有70分2333 思路分析 正解的思路很巧妙,其实我并不觉得这是个正儿八经的网络流或者二分图匹配的题目,主要还是个思维+建图模型+乱搞...... \(C=1\)时我们就可以对于每个人直接匹配而不会影响到后面的选择了.但是\(C>1\)的话,可能某一个人可以选多个导师,当他随便选了其中一个以后,可能影响到后面某个人使其选不到本来的最优解.而此时后面那个人就要让前面那个人改变决策,做出反悔. 这时候有没有想到网

[luogu] P4364 [九省联考2018]IIIDX(贪心)

P4364 [九省联考2018]IIIDX 题目背景 Osu 听过没?那是Konano 最喜欢的一款音乐游戏,而他的梦想就是有一天自己也能做个独特酷炫的音乐游戏.现在,他在世界知名游戏公司KONMAI 内工作,离他的梦想也越来越近了. 这款音乐游戏内一般都包含了许多歌曲,歌曲越多,玩家越不易玩腻.同时,为了使玩家在游戏上氪更多的金钱花更多的时间,游戏一开始一般都不会将所有曲目公开,有些曲目你需要通关某首特定歌曲才会解锁,而且越晚解锁的曲目难度越高. 题目描述 这一天,Konano 接到了一个任务

【BZOJ5248】【九省联考2018】一双木棋(搜索,哈希)

[BZOJ5248][九省联考2018]一双木棋(搜索,哈希) 题面 BZOJ Description 菲菲和牛牛在一块n行m列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手.棋局开始时,棋盘上没有任何棋子, 两人轮流在格子上落子,直到填满棋盘时结束.落子的规则是:一个格子可以落子当且仅当这个格子内没有棋子且 这个格子的左侧及上方的所有格子内都有棋子. 棋盘的每个格子上,都写有两个非负整数,从上到下第i行中从左到右第j列的格子上的两个整数记作Aij.Bij.在 游戏结束后,菲菲和牛牛会分别计算自己