[loj 2478][luogu P4843]「九省联考 2018」林克卡特树

传送门

Description

小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 能得到的 总收益 - 总过路费最大是多少。

Solution

原题转化为树上求K+1条不相交路径的最大权值和

是一道WQS二分裸题

首先,可以很容易的写出一个树形dp

然后根据wqs的套路,给每条路径的权值和+add

在计算最大权值和时,我们应当考虑让选到的边数尽可能的大

其实就是在写树d的时候适当注意一下顺序就行

然后,WQS二分出的应该时最小的——使得所选点数\(\geq K+1\)的add

Code?

#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
const ll inf=3e11,MN=3e5+5;
int N,K,hr[MN],en;
struct edge{int to;ll w;int nex;}e[MN<<1];
inline void ins(int f,int t,int w)
{
    e[++en]=(edge){t,w,hr[f]};hr[f]=en;
    e[++en]=(edge){f,w,hr[t]};hr[t]=en;
}
ll ans,dec;
struct dp{
    ll v;int c;
    dp(ll _v=0,ll _c=0):v(_v),c(_c){}
    dp operator+(const dp o){return dp(v+o.v,c+o.c);}
    dp operator-(const dp o){return dp(v-o.v,c-o.c);}
    dp operator*(const dp o){return dp(v+o.v-dec,c+o.c-1);}
    dp operator+(const ll o){return dp(v+o,c);}
}f[MN][3];
dp Max(dp o,dp oo){if(o.v>oo.v)return o;return oo;}
void dfs(int x,int fa)
{
    register int i;

    for(i=hr[x];i;i=e[i].nex)if(fa^e[i].to)
    {
        dfs(e[i].to,x);
        f[x][2]=Max(f[x][2]+f[e[i].to][0],f[x][1]+f[e[i].to][1]+dp(e[i].w-dec,-1));
        f[x][1]=Max(f[x][0]+f[e[i].to][1]+e[i].w,f[x][1]+f[e[i].to][0]);
        f[x][0]=f[x][0]+f[e[i].to][0];
    }
    f[x][0]=Max(f[x][0],Max(f[x][1],f[x][2]));
}
inline void check(ll mid)
{
    register int i;dec=mid;
    for(i=1;i<=N;++i) f[i][0]=dp(0,0),f[i][2]=f[i][1]=dp(dec,1);
    dfs(1,0);
}
int main()
{
    N=read();K=read()+1;
    register int i,x,y;
    for(i=1;i<N;++i) x=read(),y=read(),ins(x,y,read());
    ll l=-inf,r=inf,mid;
    for(;l<=r;f[1][0].c>=K?r=mid-1:l=mid+1)
    {
        mid=(l+r)>>1;check(mid);
        if(f[1][0].c>=K)ans=f[1][0].v-1ll*f[1][0].c*mid;
    }
    return 0*printf("%lld\n",ans);
}


Blog来自PaperCloud,未经允许,请勿转载,TKS!

原文地址:https://www.cnblogs.com/PaperCloud/p/10493261.html

时间: 2024-11-06 03:54:22

[loj 2478][luogu P4843]「九省联考 2018」林克卡特树的相关文章

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

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

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

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

九省联考 2018 游记

Day0:乘火车到了上海.明天就是激动人心的比赛啦 深夜和室友看<我在七年后等你>.这真是一款不错的手游,让人印象深刻啊 Day1:迷迷糊糊到了学校.编程环境是Win7?不太习惯啊. T1:一眼状压dp题. T2:肯定可以建成一棵树,然后直接贪心?不对啊,T2不应该这么水啊(开始怀疑) T3:乍一看怎么一点思路没有啊. 8:40~11:10:持续思考T3中. 11:10:终于有思路了!如果直接NTT向上dp的话,因为链的情况复杂度会不对,所以似乎可以树剖!用线段树分治和NTT处理重链上的dp!

luogu P4365 [九省联考2018]秘密袭击coat

luogu 这里不妨考虑每个点的贡献,即求出每个点在多少个联通块中为第\(k\)大的(这里权值相同的可以按任意顺序排大小),然后答案为所有点权值\(*\)上面求的东西之和 把比这个点大的点看成\(1\),小于等于他的看成\(0\),那么就是要求出包含枚举的那个点并且权值和为\(k-1\)的联通块个数,可以树型\(dp\),设\(f_{x,j}\)表示联通块最上面的点为\(x\)并且权值和为\(j\)的联通块数,转移树型背包即可,具体细节见代码.复杂度可以做到\(O(nk)\) 所以总复杂度为\(

[BZOJ5248][九省联考2018]双木棋chess

bzoj luogu sol 首先,要保证一个格子的左边和上方都放满了棋子,就需要这个点的左上方那个矩形都放满了棋子. 这样放旗子状态就会是一个自左下至右上的轮廓线. 状态数? 跟\(yyb,ppl\)讨论了一下状态数理论上应该是\(C_{20}^{10}\)啊. 然而... #include<cstdio> #include<algorithm> using namespace std; int n,m,a[20],tot; void dfs(int u) { if (u==n+

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

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

bzoj5250 [九省联考2018]秘密袭击coat 树形dp

题目背景 We could have had it all. . . . . . 我们本该,拥有一切 Counting on a tree. . . . . . 何至于此,数数树上 Counting on a Tree( CoaT)即是本题的英文名称. 题目描述 AccessGlobe最近正在玩一款战略游戏.在游戏中,他操控的角色是一名C国士兵.他的任务就是服从指挥官的指令 参加战斗,并在战斗中取胜.C国即将向D国发动一场秘密袭击.作战计划是这样的:选择D国的s个城市,派出C国 战绩最高的s个士

[九省联考2018]IIIDX

传送门 形式化题意:一棵树,对于每个节点赋予一个给定的权值,使得每个节点都不大于子树内节点,同时满足编号小的点尽可能大. 首先在所有给定的数不同的时候只要贪心一次,从小到大把数排序,之后建树在上面跑dfs,按dfn从小到大给权值. 但是这样在有相同的数据的时候是会错的.因为有可能通过交换使得子树内节点权值和根相等.(例子在luogu讨论区有的) 我们重新考虑一下.把所有的数从大到小排序,记录\(C_i\)为每个点左边还能被使用的权值个数.这样的话每次找一个位置赋权值的话,其实就是找一个位置使得它

[九省联考2018]秘密袭击coat 伪&#183;题解

爆算碾标程实例 不太会多项式……不太会线段树合并 那就只能O(n^2*w^2)爆算+乱搞优化(见代码) (这里网上都说是O(n*w^2),我不太明白,也许是我算的不对,望有识之士教我) 愣是卡进luogu最优解第3页 自以为要卡常数,结果卡了好久以后发现是死循环…… #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; co