• 【洛谷P3647】[APIO2014]连珠线


    传送门

    前言

    对于换根的理解应该和其他题解不一样,求过。

    题解

    首先分析题目简化题意:给定一棵树,从里面选出若干个“三连点”的边,使边权和最大。其中“三连边”有如下图两种形态:(3-1-2)(3-5-6)
    image
    (图源:tommymio)
    一开始我想到一种 DP:(dp(u,0/1/2)) 表示 (u) 节点的子树内可以向下延伸的边数为 (0/1/2) 时的最大值,希望通过类似容斥解决形如 (3-1-2) 的情况,但实际不太可行(也可能只是我没做出来)。
    换一种思路,(dp(u,0/1)) 表示 (u) 是/不是蓝线中点时,子树答案的最大值。

    • 如果 (u) 不是中点,那对于一个儿子 (v)(v) 可以是中点也可以不是中点,就有转移式:(dp(u,0)=max{dp(v,1)+w,dp(v,0)})
    • 如果 (u) 是中点,其实是对于它的一个儿子,(u) 是中点,进行“(u) 是中点”的转移,即 (dp(v,0)+w);但是对于其他儿子 (u) 依旧不是中点,所以转移同上。综合一下就是 (dp(u,1)=dp(u,0)+max{dp(v,0)+w-max{dp(v,1)+w,dp(v,0)}})

    换根!

    但是这样做只考虑了竖着的“三连点”,可以发现通过不断换根的位置,可以使树里所有 (3-1-2) 形态的选法都变成竖着的。
    不能枚举根,所以考虑换根。
    记录 (up(u,0/1)) 表示 (u) 是/不是蓝线中点时,子树答案的最大值。(换根 DP 都这么设,我这里的子树外是指刨去子树的其他部分)。
    先看图:
    image
    考虑当前从点 (u) 向下走到儿子 (v),更新 (up(v,0/1)),显然是从红色部分和蓝色部分来更新。其中蓝色部分是和 (up(u)) 有关,红色部分是和 (dp(u),dp(v)) 有关。

    • 对于红色部分,相当于从 (dp(u)) 里撤销 (v) 子树的贡献,根据前面的转移可以发现贡献是 (dp(u,0)-max{dp(v,0),dp(v,1)+w})注意这里先用 (u) 不是中点的情况转移,因为 (u) 是中点的情况只是相当于多选了一个儿子与他相连(换根相当于此时把 (v) 看成根)
      现在考虑加上 (u) 是中点的情况,此时应该从红色部分里面选出当初转移加上的 (max{dp(v,0)+w-max{dp(v,1)+w,dp(v,0)}}),然而此时子树 (v) 已经被删掉,所以最大值可能不存在,所以在第一个 DFS 转移时应该同时记录转移加上的最大值和次大值,同时原来的父亲 (u) 也可以向 (v) 进行中点的连边转移,而这个贡献是 (up(u,1)-up(u,0))
    • 对于蓝色部分,比较简单,贡献就是 (up(u,0)+trans)。其中 (trans) 为转移变量。具体地,看下面的总转移式。

    总转移式(太复杂就贴在代码块里了):

    up[v][0]=dp[u][0]-max(dp[u][0],dp[u][1]+w)+up[u][0]+max(0ll,trans+w);
    up[v][1]=dp[u][0]-max(dp[u][0],dp[u][1]+w)+up[u][0]+w;
    

    稍微解释一下为什么可以都从 (u) 不作为中点(即第二维为 (0))的情况转移,因为 (max{0,trans+w}>0) 就意味着从 (up(u,1)) 或者x一个其他的儿子 (v)(dp(v,1)) 转移过来。
    最后有一点细节要注意,对于叶子节点作为中点的情况初始化赋值成极小值,表示不合法方案。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    #define ll long long
    const int INF = 0x3f3f3f3f,N = 2e5+10;
    inline ll read()
    {
    	ll ret=0;char ch=' ',c=getchar();
    	while(!(c>='0'&&c<='9')) ch=c,c=getchar();
    	while(c>='0'&&c<='9') ret=(ret<<1)+(ret<<3)+c-'0',c=getchar();
    	return ch=='-'?-ret:ret;
    }
    int n,ecnt=-1,head[N]; 
    inline void init()
    {
    	memset(head,-1,sizeof(head));
    	ecnt=-1;
    }
    struct edge
    {
    	int nxt,to,w;
    }a[N<<1];
    inline void add_edge(const int x,const int y,const int w)
    {
    	a[++ecnt]=(edge){head[x],y,w};
    	head[x]=ecnt;
    }
    int dp[N][2],up[N][2];
    int f[N][2],hson[N];
    void dfs1(const int u,const int fa)
    {
    	int maxn=-2e9,cnt_son=0;
    	for(int i=head[u];~i;i=a[i].nxt)
    	{
    		int v=a[i].to;
    		if(v==fa) continue; 
    		cnt_son++;
    		dfs1(v,u);
    		int tmp=max(dp[v][0],dp[v][1]+a[i].w);
    		int trans=dp[v][0]+a[i].w-max(dp[v][0],dp[v][1]+a[i].w);
    		dp[u][0]+=tmp;
    		if(maxn<trans)
    		{
    			f[u][1]=maxn;
    			hson[u]=v;
    			maxn=trans;
    		}
    		else f[u][1]=max(f[u][1],trans);
    	}
    	dp[u][1]=dp[u][0]+maxn;
    	if(!cnt_son) dp[u][1]=-2e9;//特判叶子为不合法 
    	f[u][0]=maxn;//由于这里的赋值,maxn要初始化成-INF而不能是0,用来表示这种转移不合法 
    }
    void dfs2(const int u,const int fa)
    {
    	for(int i=head[u];~i;i=a[i].nxt)
    	{
    		int v=a[i].to;
    		if(v==fa) continue;
    		int trans=up[u][1]-up[u][0];//trans是什么意思?下面为什么取max? 
    		//A:相当于多了一个父亲连边的情况,和原来儿子连边的情况取max,为下面转移做准备 
    		if(hson[u]==v) trans=max(trans,f[u][1]);
    		else trans=max(trans,f[u][0]);
    		
    		int tmp=max(dp[v][0],dp[v][1]+a[i].w);
    		
    		up[v][0]=dp[u][0]-tmp+up[u][0]+max(0ll,trans+a[i].w);
    		up[v][1]=dp[u][0]-tmp+up[u][0]+a[i].w;
    		dfs2(v,u);
    	}
    }
    signed main()
    {
    	memset(head,-1,sizeof(head));
    	n=read();
    	for(int i=1;i<n;i++)
    	{
    		int u=read(),v=read(),w=read();
    		add_edge(u,v,w),add_edge(v,u,w);
    	}
    	dfs1(1,-1); 
    	up[1][0]=0,up[1][1]=-2e9;//特判叶子为不合法 
    	dfs2(1,-1);
    	int ans=0;
    	for(int i=1;i<=n;i++)	
    		ans=max(ans,max(dp[i][0]+up[i][0],dp[i][1]+up[i][1]));
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    Python之路【第七十一篇】:django日更
    Python之路【第七十篇】:django日更
    Python之路【第六十九篇】:django日更
    选包
    Python之路【第六十八篇】:django日更
    Python之路【第六十七篇】:django日更
    Python之路【第六十六篇】:django日更
    Python之路【第六十五篇】:django日更
    Python之路【第六十四篇】:django日更
    Python之路【第六十三篇】:django日更
  • 原文地址:https://www.cnblogs.com/conprour/p/15473439.html
Copyright © 2020-2023  润新知