ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[ZJOI2008] 骑士

2021-07-03 16:03:54  阅读:169  来源: 互联网

标签:vis int max fa DP ZJOI2008 骑士 dp


[ZJOI2008]骑士


题意简述

求基环树森林中的最大权独立集。

数据范围:\(1\le n\le 10^6\)。


知识要点

  • 树形 DP
  • 基环树

题目分析

题目一上眼,是一个显然的最大权独立集问题。这是树形 DP 中选择节点类的题目,如果整个图构成一颗树的话,可以直接套路地定义状态 \(dp[i][0]/dp[i][1]\),表示第 \(i\) 个节点,是否选择,得到的以 \(i\) 为根节点的树中的最大权独立集。

容易写出状态转移方程:

\[\begin{aligned}\begin{cases} dp[i][0]&=\sum\limits_{x\in son[i]} \max\Big(dp[x][0],dp[x][1]\Big)\\dp[i][1]&=\sum\limits_{x\in son[i]}dp[x][0]\end{cases}\end{aligned}\]

边界条件:\(dp[i][0]=0,dp[i][1]=val[i]\)

但在此题中,我们需要在基环树森林中求最大权独立集。注意到图中仅有 \(n\) 个点 \(n\) 条边,因此图中有且仅有一个环。而断开这个环上的任意一条边,剩下的点和边就形成了一颗树,我们就可以用树形 DP 进行统计了。

要注意,虽然我们把边断开了,但是边两端的节点 \(u\) 和 \(v\) 仍然不能同时选择。因此我们需要采取强制措施,对断边的两端 \(u\) 和 \(v\) 都跑一次树形 DP,并且在从 \(u\) 开始时不经过 \(v\),从 \(v\) 开始时不经过 \(u\)。

为了便于操作,我们从骑士的敌人向骑士连一条有向边,因为一名骑士只有一个自己最厌恶的骑士,因此连边后整张图就构成了基环外向树。因此当跑树形 DP 重新经过出发点 \(u\) 时,那么这条边的起点就是 \(v\),也就是不能选的节点。出现此种情况时,我们设置 \(dp[v][1]=-inf\),就可以防止选取点 \(v\) 了。

如何找到环?在这里借用一下 hkr04 洛谷题解 中的图片:

在找环时,当我们发现 7 号点没有被访问时,说明这棵树的答案仍为统计。我们从该点往回回到环上,最终到达了 2 号点。


代码展示

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const ll inf=0x3f3f3f3f3f3f3f3f;
const int M=1e6+5;

int n,fa[M],p0;
bool vis[M];
ll val[M],dp[M][2],ans;

struct edge
{
	int to,nxt;
}e[M];
int hd[M],cnt;
void add(int x,int y)
{
	e[++cnt]=(edge){y,hd[x]};
	hd[x]=cnt;
}

void dfs(int u)
{
	vis[u]=true;
	dp[u][0]=0,dp[u][1]=val[u];// 边界条件
	
	for(int i=hd[u];i;i=e[i].nxt)
	{
		int v=e[i].to;
		if(v==p0)dp[v][1]=-inf;// 强制控制v不选
		else
		{
			dfs(v);
			dp[u][0]+=max(dp[v][0],dp[v][1]);
			dp[u][1]+=dp[v][0];// 状态转移
		}
	}
}

void find_circle(int u)
{
	vis[u]=true;
	while(!vis[fa[u]])
	{
		u=fa[u];
		vis[u]=true;
	}
	p0=u,dfs(u);
	ll now=max(dp[u][0],dp[u][1]);
	u=fa[u];//执行该语句前有 vis[fa[u]]=true,因此u和fa[u]就是需要断边的两端
	p0=u,dfs(u);
	now=max(now,max(dp[u][0],dp[u][1]));
	ans+=now;
}

int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld%d",&val[i],&fa[i]);
		add(fa[i],i);//注意从敌人连向自己
	}
	for(int i=1;i<=n;i++)
		if(!vis[i])//被统计过了就不搜了
			find_circle(i);
	printf("%lld",ans);
	return 0;
}

标签:vis,int,max,fa,DP,ZJOI2008,骑士,dp
来源: https://www.cnblogs.com/cyl06/p/AC1000.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有