一、题目
二、解法
这道题的图一定是若干个不连通的基环树,考虑到一条边的限制是两边不能同时选,我们先将一个边的限制确定了,再对整棵树跑没有上司的舞会,所以我们就取环上的一对点,跑两次 d p dp dp,每次限制某一个点一定不能选,这道题细节有点多,详细看代码。
#include <cstdio>
#include <iostream>
using namespace std;
#define int long long
#define inf 0x3f3f3f3f3f3f3f3f
const int MAXN = 1000005;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,x,y,tot,ans,f[MAXN],pd[MAXN],vis[MAXN];
int vis2[MAXN],val[MAXN],dp[MAXN][2];//0/1 选/不选
struct edge
{
int v,next;
}e[MAXN*2];
void find(int u,int fa)//找环
{
vis[u]=pd[u]=1;//vis判断是否访问,pd找环只找反祖边(卡掉二元环)
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v;
if(v==fa) continue;
if(pd[v])//找到了返祖边,有环
{
x=u;y=v;
continue;
}
if(vis[v]) continue;//只能向没有标记的地方跑
find(v,u);
pd[v]=0;//只能找返祖边,所以清零
}
}
void dfs(int u,int fa)
{
vis2[u]=1;//判断访问过没有
if(dp[u][0]!=-inf) dp[u][0]=val[u];
dp[u][1]=0;//赋初值
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v;
if(vis2[v] || (u==x && v==y) || (u==y && v==x)) continue;
//只能走没访问过的点,不能走返祖边
dfs(v,u);
dp[u][0]+=dp[v][1];//没有上司的舞会
dp[u][1]+=max(dp[v][1],dp[v][0]);
}
}
void clear(int u,int fa)//清空vis2
{
vis2[u]=0;
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v;
if(vis2[v]) clear(v,u);
}
}
signed main()
{
n=read();
for(int i=1;i<=n;i++)
{
val[i]=read();int j=read();
e[++tot]=edge{j,f[i]},f[i]=tot;
e[++tot]=edge{i,f[j]},f[j]=tot;
}
for(int i=1;i<=n;i++)
if(!vis[i])
{
int Max=0;
x=y=0;
find(i,0);
dp[x][0]=-inf;//限制x不能选
dfs(i,0);
clear(i,0);//清空vis2
Max=max(Max,max(dp[i][0],dp[i][1]));
dp[x][0]=0;//清除标记
dp[y][0]=-inf;//限制y不能选
dfs(i,0);
Max=max(Max,max(dp[i][0],dp[i][1]));
dp[y][0]=0;
ans+=Max;//每个基环树的最大值求和
}
printf("%lld\n",ans);
}