• 【洛谷P4383】林克卡特树


    题目

    题目链接:https://www.luogu.com.cn/problem/P4383
    小 L 最近沉迷于塞尔达传说:荒野之息(The Legend of Zelda: Breath of The Wild)无法自拔,他尤其喜欢游戏中的迷你挑战。
    游戏中有一个叫做 LCT 的挑战,它的规则是这样子的:现在有一个 (N) 个点的树,每条边有一个整数边权 (v_i),若 (v_i geq 0),表示走这条边会获得 (v_i) 的收益;若 (v_i lt 0) ,则表示走这条边需要支付 (-v_i) 的过路费。小 L 需要控制主角 Link 切掉(Cut)树上的恰好 (K) 条边,然后再连接 (K) 条边权为 0 的边,得到一棵新的树。接着,他会选择树上的两个点 (p,q),并沿着树上连接这两点的简单路径从 (p) 走到 (q),并为经过的每条边支付过路费/ 获取相应收益。
    海拉鲁大陆之神 TemporaryDO 想考验一下 Link。他告诉 Link,如果 Link 能切掉合适的边、选择合适的路径从而使 总收益 - 总过路费 最大化的话,就把传说中的大师之剑送给他。
    小 L 想得到大师之剑,于是他找到了你来帮忙,请你告诉他,Link 能得到的 总收益 - 总过路费 最大是多少。
    (n,kleq 3 imes 10^5)(|v_i|leq 10^6)

    思路

    问题等价于在树上选择 (k) 条不交路径,使得它们的权值之和最大。
    (f[x][i][0/1/2]) 表示点 (x) 的子树内,选择了 (i) 条路径,其中点 (x) 相连的边选择了 (0/1/2) 条的最大权值和。
    转移的话,就考虑新加入 (x) 的一个儿子 (y),讨论一下这条边选不选就行了。时间复杂度 (O(nk^2))
    这样显然过不了。题解愉快的告诉我们,随着 (k) 不断增大,(f) 是一个凸函数,直接上 WQS 二分。二元组 (f[x][0/1/2]) 表示点 (x) 的子树内,选择若干条路径,其中点 (x) 相连的边选择了 (0/1/2) 条的最大权值和,以及此时选择的路径最多是多少条。
    至于为什么 (f) 是一个凸函数我也不清楚,反正出题人说是就是吧(什
    时间复杂度 (O(nlog (nV)))(V) 是值域。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=300010;
    const ll Inf=1e18;
    int n,m,tot,head[N];
    ll ans;
    
    struct edge
    {
    	int next,to,dis;
    }e[N*2];
    
    struct node
    {
    	ll a,b;
    	
    	friend node operator +(node x,node y)
    	{
    		return (node){x.a+y.a,x.b+y.b};
    	}
    }f[N][3];
    
    node max(node x,node y)
    {
    	if (x.a>y.a) return x;
    	if (x.a<y.a) return y;
    	return (node){x.a,max(x.b,y.b)};
    }
    
    void add(int from,int to,int dis)
    {
    	e[++tot]=(edge){head[from],to,dis};
    	head[from]=tot;
    }
    
    void dfs(int x,int fa,ll mid)
    {
    	f[x][0]=(node){0,0}; f[x][1]=(node){-Inf,0}; f[x][2]=(node){-mid,1};
    	for (int i=head[x];~i;i=e[i].next)
    	{
    		int v=e[i].to;
    		if (v!=fa)
    		{
    			dfs(v,x,mid);
    			f[x][2]=max(f[x][2],f[x][2]+max(f[v][0],max(f[v][1],f[v][2])));
    			f[x][2]=max(f[x][2],f[x][1]+max(f[v][0]+(node){e[i].dis,0},f[v][1]+(node){e[i].dis+mid,-1}));
    			f[x][1]=max(f[x][1],f[x][1]+max(f[v][0],max(f[v][1],f[v][2])));
    			f[x][1]=max(f[x][1],f[x][0]+max(f[v][1]+(node){e[i].dis,0},f[v][0]+(node){e[i].dis-mid,1}));
    			f[x][0]=max(f[x][0],f[x][0]+max(f[v][0],max(f[v][1],f[v][2])));
    		}
    	}
    }
    
    int main()
    {
    	memset(head,-1,sizeof(head));
    	scanf("%d%d",&n,&m);
    	m++;
    	for (int i=1,x,y,z;i<n;i++)
    	{
    		scanf("%d%d%d",&x,&y,&z);
    		add(x,y,z); add(y,x,z);
    	}
    	ll l=-3e11,r=3e11,mid;
    	while (l<=r)
    	{
    		mid=(l+r)>>1;
    		dfs(1,0,mid);
    		node res=max(f[1][0],max(f[1][1],f[1][2]));
    		if (res.b>=m) l=mid+1,ans=res.a+res.b*mid;
    			else r=mid-1;
    	}
    	cout<<ans;
    	return 0;
    }
    
  • 相关阅读:
    django笔记
    pandas dataframe的合并(append, merge, concat)
    pandas删除行删除列,增加行增加列
    github上值得关注的前端项目
    CSS布局奇淫技巧之--各种居中
    级联菜单
    鼠标移入移出改变透明度
    图片轮播特效
    图片放大镜效果
    css3多列布局
  • 原文地址:https://www.cnblogs.com/stoorz/p/15190761.html
Copyright © 2020-2023  润新知