题目:
题意:
我们有两种操作
1.
(
u
,
v
)
1.(u,v)
1.(u,v)视为红绳
2.
(
u
,
v
,
k
)
2.(u,v,k)
2.(u,v,k)表示将
k
k
k插入到
(
u
,
v
)
(
(u,v)(
(u,v)(一定要是红绳
)
)
)中间,
(
u
,
k
)
、
(
k
,
v
)
(u,k)、(k,v)
(u,k)、(k,v)视为蓝绳,并计入到答案
我们现在有
n
n
n个端点,知道最后哪两个端点间有连线,并最后呈现一棵树的形态
问我们如何安排操作,能使得答案最大,求这个最大答案是多少
分析:
挺考思维的吧(也可能是我菜
因为我们原本是无根的无环连通的无向图,我们不妨随便找个点定为根,看下此时的答案会是怎么样的
在此之前,通过题意,我们可以了解到一个端点,最多只能是两根蓝绳的交界点,也就是要是不是蓝绳的中间点,要么是,也只能充当一次中间点
如此一来设
f
u
,
0
f_{u,0}
fu,0表示在
u
u
u点处,
u
u
u不是中间点的最大答案,
f
u
,
1
f_{u,1}
fu,1表示是中间点的
f
u
,
0
=
∑
v
ϵ
u
s
o
n
m
a
x
{
f
v
,
0
,
f
v
,
1
+
w
}
f_{u,0}=\sum_{v\ \epsilon\ u_{son}} max\{f_{v,0},f_{v,1}+w\}
fu,0=v ϵ uson∑max{fv,0,fv,1+w}
而
f
u
,
1
f_{u,1}
fu,1则是将连向某一个子节点的线变成蓝绳,而其他的跟红绳一样计算
f
u
,
1
=
f
u
,
0
+
m
a
x
{
f
v
,
0
+
w
−
m
a
x
{
f
v
,
0
,
f
v
,
1
+
w
}
}
f_{u,1}=f_{u,0}+max\{f_{v,0}+w-max\{f_{v,0},f_{v,1}+w\}\}
fu,1=fu,0+max{fv,0+w−max{fv,0,fv,1+w}}
后面的那个减法表示减去该子节点作为红绳的贡献
现在已经考虑好确定跟的情况了,现在问题来到如何求出换根后的答案
换根
(
u
(u
(u变为
v
)
v)
v),对原本答案的影响有:
1.
v
1.v
1.v的贡献消失了
2.
u
2.u
2.u变成了
v
v
v的子节点,我们需要计算此时
u
u
u对答案的贡献
对于
f
x
,
0
f_{x,0}
fx,0,我们直接减去贡献就好了
对于
f
x
,
1
f_{x,1}
fx,1,我们需要保存最大值和次大值,当
v
v
v作为最大贡献的蓝绳的一个端点时,这条蓝绳不一定能存在了,所以需要变为次大值
u
u
u和
v
v
v都是这样更新,不要忘了在遍历完
v
v
v的子树后,对
u
u
u进行回溯,再遍历
u
u
u的下一个子节点
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
using namespace std;
inline LL read()
{
LL s=0,f=1; char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9') {s=s*10+c-'0';c=getchar();}
return s*f;
}
struct node{
int to,next,w;
}e[400005];
int ls[200005],cnt=0;
void add(int x,int y,int w)
{
e[cnt]=(node){y,ls[x],w};
ls[x]=cnt++;
return;
}
int f[200005][2];
int m1[200005],m2[200005];
void dfs(int u,int fa)
{
for(int i=ls[u];~i;i=e[i].next)
{
int v=e[i].to;
if(v==fa) continue;
dfs(v,u);
f[u][0]+=max(f[v][0],f[v][1]+e[i].w);
}
f[u][1]=f[u][0];
m1[u]=m2[u]=-2147483647;
for(int i=ls[u];~i;i=e[i].next)
{
int v=e[i].to;
if(v==fa) continue;
int bc=f[v][0]+e[i].w-max(f[v][0],f[v][1]+e[i].w);
if(bc>m1[u]) m2[u]=m1[u],m1[u]=bc;
else if(bc>m2[u]) m2[u]=bc;
}
f[u][1]+=m1[u];
return;
}
int ans=-2147483647;
void change(int u,int fa)
{
ans=max(ans,f[u][0]);
int b0=f[u][0],b1=f[u][1],b2=m1[u],b3=m2[u];
for(int i=ls[u];~i;i=e[i].next)
{
int v=e[i].to;
if(v==fa) continue;
f[u][0]-=max(f[v][0],f[v][1]+e[i].w);
f[u][1]-=max(f[v][0],f[v][1]+e[i].w);
int b=f[v][0]+e[i].w-max(f[v][0],f[v][1]+e[i].w);
if(b==m1[u]) f[u][1]+=m2[u]-m1[u];
f[v][0]+=max(f[u][0],f[u][1]+e[i].w);
f[v][1]+=max(f[u][0],f[u][1]+e[i].w);
f[v][1]-=m1[v];
b=f[u][0]+e[i].w-max(f[u][0],f[u][1]+e[i].w);
if(b>m1[v]) m2[v]=m1[v],m1[v]=b;
else if(b>m2[v]) m2[v]=b;
f[v][1]+=m1[v];
change(v,u);
f[u][0]=b0;f[u][1]=b1;
m1[u]=b2;m2[u]=b3;
}
return;
}
int main()
{
memset(ls,-1,sizeof(ls));
int n=read();
for(int i=1;i<n;i++)
{
int x=read(),y=read(),w=read();
add(x,y,w);add(y,x,w);
}
dfs(1,0);
change(1,0);
cout<<ans;
return 0;
}

这篇博客探讨了一种图的构造问题,其中涉及两种操作:红绳连接和蓝绳插入。目标是找到最大化蓝绳数量的策略,使最终形成一棵树。博主分析了如何通过动态规划和换根策略求解此问题,并提供了相应的C++代码实现。文章深入浅出地解释了算法思路,包括状态转移方程和递归过程。
468

被折叠的 条评论
为什么被折叠?



