在基环树上连边的两点中最多选取一点,求选取点权和最大值。 n ≤ 1 0 5 n\leq 10^5 n≤105 。
这是一道基环树DP的简单题,但我却用树形DP解决了这个问题。首先,如果把题目中的 n n n 变成 n − 1 n-1 n−1 就是没有上司的舞会了。那么问题能否转化呢,答案是肯定的。
在建边的时候用并查集查环,若 find
得到的结果相同就直接拆掉这条边,得到一棵树。然后直接以两个点为根进行2次树形DP,得到两次
f
[
r
o
o
t
]
[
0
/
1
]
f[root][0/1]
f[root][0/1] 表示有或没有
r
o
o
t
root
root 时的点权和最大。因为要2选1,最后就直接得到两次
f
[
r
o
o
t
]
[
0
]
f[root][0]
f[root][0] 中较大的一个,表示不选择这条边时的最大(具体另一条选不选没有关系),即为答案。
#include<bits/stdc++.h>
using namespace std;
int n,p[100005],head[100005],fa[100005],ecnt,ll,rr,dp[100005][2];
double k;
struct edge{
int nxt,to;
}e[200005];
void adde(int u,int v){
e[++ecnt].nxt=head[u];
e[ecnt].to=v;
head[u]=ecnt;
}
int find(int x){
return fa[x]==x?x:fa[x]=find(fa[x]);
}
void dfs(int x,int fa){
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==fa)
continue;
dfs(y,x);
dp[x][0]+=max(dp[y][0],dp[y][1]);
dp[x][1]+=dp[y][0];
}
dp[x][1]+=p[x];
return;
}
int main(){
scanf("%d",&n);
for(int i=0;i<=n-1;++i){
scanf("%d",&p[i]);
fa[i]=i;
}
for(int i=1;i<=n;++i){
int ui,vi;
scanf("%d %d",&ui,&vi);
if(find(ui)!=find(vi)){
fa[find(ui)]=find(vi);
adde(ui,vi);
adde(vi,ui);
}
else{
ll=ui,rr=vi;
}
}
scanf("%lf",&k);
dfs(ll,ll);
int ans=dp[ll][0];
memset(dp,0,sizeof(dp));
dfs(rr,rr);
ans=max(ans,dp[rr][0]);
printf("%.1lf",ans*k);
return 0;;
}