BZOJ1040&&洛谷P2607 [ZJOI2008]骑士

一道树形dp,但是有坑点,就是他可能是个环,所以我们要将环拆成链,然后我们发现从链的首尾进去都可以是一条链,所以我们要从首尾进去,取max

代码

//By AcerMo
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lli long long int
using namespace std;
const int M=1e6+50;
bool vis[M*2];
lli f[M][2],ans,e;
int rt,trt,n,c[M],fl;
int to[M*2],nxt[M*2],head[M],cnt,id[M*2];
inline int read()
{
	lli x=0;char ch=getchar();
	while (ch>'9'||ch<'0') ch=getchar();
	while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return x;
} 
inline void add(int x,int y,int idy)
{
	to[++cnt]=y;nxt[cnt]=head[x];head[x]=cnt;id[cnt]=idy;//记录边的编号
	to[++cnt]=x;nxt[cnt]=head[y];head[y]=cnt;id[cnt]=idy;//防止走断掉的边 
	return ;
}
inline void dfs(int x,int fa)
{
	vis[x]=1;
	for (int i=head[x];i;i=nxt[i])
	{
		if (to[i]==fa) continue;
		if (vis[to[i]])
			rt=to[i],trt=x,fl=id[i];//一个环的两个端点,断环
		else dfs(to[i],x);
	}
	return ;
}
inline void tdp(int x,int fa)
{
	f[x][0]=0;f[x][1]=c[x];
	for (int i=head[x];i;i=nxt[i])
	{
		if (id[i]==fl||to[i]==fa) continue;
		tdp(to[i],x);f[x][1]+=f[to[i]][0];
		f[x][0]+=max(f[to[i]][0],f[to[i]][1]);
	}
	return ;
}
signed main()
{
	n=read();int x;
	for (int i=1;i<=n;i++)
		c[i]=read(),x=read(),add(x,i,i);
	for (int i=1;i<=n;i++)
	if (!vis[i])
	{
		dfs(i,0);tdp(rt,0);e=f[rt][0];
		tdp(trt,0);e=max(e,f[trt][0]);
		//看环的首还是尾不选,取max 
		ans+=e;
	}
	cout<<ans;
	return 0; 
}
//-----------------------------------------------------------------------------------------------------------------
//     ===============       #             #      ||\\    		||        ||============       || ==========
//     ===============       #             #      || \\  		||        ||                   ||//        \\
//           | |             #             #      ||  \\        ||        ||                   ||           \\
//           | |             #             #      ||   \\       ||        ||                   ||           ||
//           | |             #             #      ||    \\      ||        ||                   ||           ||
//           | |             #             #      ||     \\     ||        ||============       ||\\=========//
//           | |             #             #      ||      \\    ||        ||                   || \\
//           | |             #             #      ||       \\   ||        ||                   ||  \\ 
//           | |             \             /      ||        \\  ||        ||                   ||   \\
//       \ \ / /              \           /       ||         \\ ||        ||                   ||    \\
//        \__ /                ===========        ||          \\||        ||============       ||     \\
//----------------------------------------------------------------------------------------------------------------

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值