[基环树DP]P1453

在基环树上连边的两点中最多选取一点,求选取点权和最大值。 n ≤ 1 0 5 n\leq 10^5 n105


这是一道基环树DP的简单题,但我却用树形DP解决了这个问题。首先,如果把题目中的 n n n 变成 n − 1 n-1 n1 就是没有上司的舞会了。那么问题能否转化呢,答案是肯定的。

在建边的时候用并查集查环,若 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;;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值