题目
Luogu
在达芬奇时代,有一个流行的儿童游戏称为连珠线。当然,这个游戏是关于珠子和线的。线是红色或蓝色的,珠子被编号为
1
1
1 到
n
n
n。这个游戏从一个珠子开始,每次会用如下方式添加一个新的珠子:
A
p
p
e
n
d
(
w
,
v
)
Append(w, v)
Append(w,v):一个新的珠子
w
w
w 和一个已经添加的珠子
v
v
v 用红线连接起来。
I
n
s
e
r
t
(
w
,
u
,
v
)
Insert(w, u, v)
Insert(w,u,v):一个新的珠子
w
w
w 插入到用红线连起来的两个珠子
u
,
v
u,v
u,v 之间。具体过程是删去
u
,
v
u,v
u,v 之间红线,分别用蓝线连接
u
,
w
u,w
u,w 和
w
,
v
w,v
w,v。
每条线都有一个长度。游戏结束后,你的最终得分为蓝线长度之和。
给你连珠线游戏结束后的游戏局面,只告诉了你珠子和链的连接方式以及每条线的长度,没有告诉你每条线分别是什么颜色。
你需要写一个程序来找出最大可能得分。即,在所有以给出的最终局面结束的连珠线游戏中找出那个得分最大的,然后输出最大可能得分。
1
≤
N
≤
2
⋅
1
0
5
1\le N\le 2\cdot10^5
1≤N≤2⋅105
思路
题目理解了好久。。。
发现没有这样的
因为我们确定一开始的球之后,只会存在
f
a
−
u
−
v
fa-u-v
fa−u−v 的连边
于是可以考虑枚举一开始的球,然后当
f
a
−
u
fa-u
fa−u 这条边被占用只有当
u
u
u 为蓝线中点
于是可以列出
d
p
dp
dp 定义:
f
[
u
]
[
0
]
f[u][0]
f[u][0] :
u
u
u 子树内
u
u
u 不作为蓝线中点的最大长度
f
[
u
]
[
1
]
f[u][1]
f[u][1] :
u
u
u 子树内
u
u
u 作为蓝线中点的最大长度
注意
f
[
u
]
[
1
]
f[u][1]
f[u][1] 的答案会在
f
a
fa
fa 那里计算,所以对于根节点是没有
f
[
u
]
[
1
]
f[u][1]
f[u][1] 的
考虑转移
f [ u ] [ 0 ] = ∑ v ∈ s o n u m a x ( f [ v ] [ 0 ] , f [ v ] [ 1 ] + w u v ) f[u][0]=\sum_{v\in son_u}max(f[v][0],f[v][1]+w_{uv}) f[u][0]=∑v∈sonumax(f[v][0],f[v][1]+wuv)
然后对于 f [ u ] [ 1 ] f[u][1] f[u][1] 我们可以以 f [ u ] [ 0 ] f[u][0] f[u][0] 为基础转移,有:
f [ u ] [ 1 ] = f [ u ] [ 0 ] + m a x ( f [ v ] [ 0 ] + w u v − m a x ( f [ v ] [ 0 ] , f [ v ] [ 1 ] + w u v ) ) f[u][1]=f[u][0]+max(f[v][0]+w_{uv}-max(f[v][0],f[v][1]+w_{uv})) f[u][1]=f[u][0]+max(f[v][0]+wuv−max(f[v][0],f[v][1]+wuv))
也就是减去某个儿子,再加这个新的贡献
但是为了方便转移,我们写成这样
f [ u ] [ 0 ] = ∑ v ∈ s o n u m a x ( f [ v ] [ 0 ] , f [ v ] [ 0 ] + f [ v ] [ 1 ] + w u v ) f[u][0]=\sum_{v\in son_u}max(f[v][0],f[v][0]+f[v][1]+w_{uv}) f[u][0]=∑v∈sonumax(f[v][0],f[v][0]+f[v][1]+wuv)
然后对于 f [ u ] [ 1 ] f[u][1] f[u][1] 我们可以以 f [ u ] [ 0 ] f[u][0] f[u][0] 为基础转移,有:
f [ u ] [ 1 ] = m a x ( f [ v ] [ 0 ] + w u v − m a x ( f [ v ] [ 0 ] , f [ v ] [ 0 ] + f [ v ] [ 1 ] + w u v ) ) f[u][1]=max(f[v][0]+w_{uv}-max(f[v][0],f[v][0]+f[v][1]+w_{uv})) f[u][1]=max(f[v][0]+wuv−max(f[v][0],f[v][0]+f[v][1]+wuv))
这样
f
[
u
]
f[u]
f[u] 的两种状态就互不干扰了(不这样好像很难写)
我们的答案就是
f
[
r
o
o
t
]
[
0
]
f[root][0]
f[root][0]
如果枚举根的话则是
O
(
n
2
)
O(n^2)
O(n2) 的
考虑换根
D
p
Dp
Dp
由于
f
[
u
]
[
1
]
f[u][1]
f[u][1] 中
f
a
−
u
fa-u
fa−u 的答案是在
f
a
fa
fa 时候算的,
我们可以假设根节点也有
f
[
u
]
[
1
]
f[u][1]
f[u][1] 的状态方便转移
现在
f
[
u
]
[
0
]
f[u][0]
f[u][0] 是以
u
u
u 为根的最大值
我们可以先算出
v
v
v 对
f
[
u
]
[
0
]
f[u][0]
f[u][0] 的贡献
d = m a x ( f [ v ] [ 0 ] , f [ v ] [ 0 ] + f [ v ] [ 1 ] + w u v ) d=max(f[v][0],f[v][0]+f[v][1]+w_{uv}) d=max(f[v][0],f[v][0]+f[v][1]+wuv)
在考虑以 v v v 为根:
f [ v ] [ 0 ] = f [ v ] [ 0 ] + m a x ( f [ u ] [ 0 ] − d , f [ u ] [ 0 ] − d + f [ u ] [ 1 ] + w u v ) f[v][0]=f[v][0]+max(f[u][0]-d,f[u][0]-d+f[u][1]+w_{uv}) f[v][0]=f[v][0]+max(f[u][0]−d,f[u][0]−d+f[u][1]+wuv)
f [ v ] [ 1 ] = m a x ( f [ v ] [ 1 ] , f [ u ] [ 0 ] − d + w u v − m a x ( f [ u ] [ 0 ] − d , f [ u ] [ 0 ] − d + f [ u ] [ 1 ] + w u v ) ) f[v][1]=max(f[v][1],f[u][0]-d+w_{uv}-max(f[u][0]-d,f[u][0]-d+f[u][1]+w_{uv})) f[v][1]=max(f[v][1],f[u][0]−d+wuv−max(f[u][0]−d,f[u][0]−d+f[u][1]+wuv))
但发现这里
f
[
u
]
[
1
]
f[u][1]
f[u][1] 可能是由
v
v
v 转移过来的
于是要保留最大和次大记为
f
[
u
]
[
1
]
f[u][1]
f[u][1] 和
f
[
u
]
[
2
]
f[u][2]
f[u][2]
为了简便我们记
M x = m a x ( f [ u ] [ 0 ] − d , f [ u ] [ 0 ] − d + f [ u ] [ 1 ] + w u v ) Mx=max(f[u][0]-d,f[u][0]-d+f[u][1]+w_{uv}) Mx=max(f[u][0]−d,f[u][0]−d+f[u][1]+wuv)
则有
f [ v ] [ 1 ] = m a x ( f [ v ] [ 1 ] , f [ u ] [ 0 ] − d + w u v − M x ) f[v][1]=max(f[v][1],f[u][0]-d+w_{uv}-Mx) f[v][1]=max(f[v][1],f[u][0]−d+wuv−Mx)
然后最大值次大值讨论一下即可
#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<queue>
#include<deque>
#include<cstdio>
#include<bitset>
#include<vector>
#include<climits>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
#define ULL unsigned long long
int read(){
int f=1,x=0;char c=getchar();
while(c<'0'||'9'<c){if(c=='-')f=-1;c=getchar();}
while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
return f*x;
}
#define MAXN 200000
#define mp make_pair
#define INF 2000000001
int n;//不为中点 为中点最大/次大转移 为根最大
int f[MAXN+5][3],g[MAXN+5];
vector<pair<int,int> > G[MAXN+5];
void DFS1(int u,int fa){
f[u][0]=0,f[u][1]=f[u][2]=-INF;
for(int i=0;i<(int)G[u].size();i++){
int v=G[u][i].first,w=G[u][i].second;
if(v==fa) continue;
DFS1(v,u);
int Mx=max(f[v][0],f[v][0]+f[v][1]+w);
f[u][0]+=Mx;
if(f[v][0]+w-Mx>f[u][1])
f[u][2]=f[u][1],f[u][1]=f[v][0]+w-Mx;
else if(f[v][0]+w-Mx>f[u][2])
f[u][2]=f[v][0]+w-Mx;
}
return ;
}
int ans;
void DFS2(int u,int fa){
ans=max(ans,f[u][0]);
for(int i=0;i<(int)G[u].size();i++){
int v=G[u][i].first,w=G[u][i].second;
if(v==fa) continue;
int d=max(f[v][0],f[v][0]+f[v][1]+w),Mx;
if(f[v][0]+w-d==f[u][1])
Mx=max(f[u][0]-d,f[u][0]-d+f[u][2]+w);
else
Mx=max(f[u][0]-d,f[u][0]-d+f[u][1]+w);
f[v][0]+=Mx;
if(f[u][0]-d+w-Mx>f[v][1])
f[v][2]=f[v][1],f[v][1]=f[u][0]-d+w-Mx;
else if(f[u][0]-d+w-Mx>f[v][2])
f[v][2]=f[u][0]-d+w-Mx;
DFS2(v,u);
}
return ;
}
int main(){
n=read();
for(int i=1;i<n;i++){
int u=read(),v=read(),w=read();
G[u].push_back(mp(v,w)),G[v].push_back(mp(u,w));
}
DFS1(1,0);
//for(int i=1;i<=n;i++)
// printf("%d %d %d\n",f[i][0],f[i][1],f[i][2]);
DFS2(1,0);
printf("%d\n",ans);
return 0;
}
思考
这种题换根时候容易被绕晕,可以用这种方式思考:
1.理清变量含义
2.去除
v
v
v 对
u
u
u 的贡献
3.尝试将
u
u
u 改为
v
v
v 的子树
4.分类讨论转移
注意状态转移时还要尽量让对于同一
u
u
u 的状态转移互不影响