骑士-没有上司的舞会增强版(树上DP+基环树)

题目链接

环基树就是有环的树

骑士 A 憎恨 骑士 B,则 B 出现 A 就不会出现
而一个骑士可以被多个骑士憎恨,一个骑士最多憎恨一个人,那么把关系改为谁被谁憎恨就是经典树状图了

而每个骑士都有憎恨的人,所以有 N 条边,必定会形成环,也不一定只有一块连通图

那如何处理?

因为每个人只能憎恨一个人,即被某个人憎恨的只有一个,说明每个点的入度只能为1
对于一颗 正常的树 ,只有顶点入度为 0 ,其他点的入度已经为 1 了
说明要形成环基树 ,之能将两颗正常树的顶点互连才行

所以,任选一点不断 getfather 就一定能到达环

我们选择环中任意一对相邻的点 A B ,A 出现则 B 不出现 ,B 出现则 A 不出现 ,或者 A 、B 都不出现
总之就是不能两者同时出现
那么就要 max 分别dfs+dp两点不出现,这样能保证只会出现上述三种情况

CODE

#include <bits/stdc++.h>
using namespace std;
typedef long long int LL;
const int N=1e6+10;
LL n,ans,p[N],f[N],dp[N][2],mark;
vector<LL> edge[N];
bool vis[N];

void add(LL fa,LL to)
{
	edge[fa].push_back(to);
	f[to]=fa;
}

void dfs(LL now)
{
	vis[now]=true;
	dp[now][1]=p[now];
	dp[now][0]=0;//每次都要重置为0,因为是分别计算DP
	for(LL to:edge[now])
	{
		if(to==mark) continue;//跳过当前起始点避免循环
		dfs(to);
		dp[now][0]+=max(dp[to][0],dp[to][1]);
		dp[now][1]+=dp[to][0];
	}
}

LL sovle(LL now)
{
	vis[now]=true;//标记该树每个点,方便以后找到每个独立的连通图
	mark=now;
	LL res=0;
	while(!vis[f[mark]])//溯源找环
	{
		vis[f[mark]]=true;
		mark=f[mark];
	}
	dfs(mark);
	res=dp[mark][0];
	mark=f[mark];
	dfs(mark);
	res=max(res,dp[mark][0]);
	return res;
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>n;
	for(LL i=1;i<=n;i++)
	{
		f[i]=i;
	}
	for(LL i=1;i<=n;i++)
	{
		LL from;//被憎恨的骑士就是“上司”
		cin>>p[i]>>from;
		add(from,i);
	}
	for(LL i=1;i<=n;i++)
	{
		if(vis[i]) continue;
		ans+=sovle(i);//累加每个连通图的最大战力
	}
	cout<<ans;
	return 0;
}
posted @ 2025-04-04 11:14  石磨豆浆  阅读(10)  评论(0)    收藏  举报