链接
http://www.lydsy.com/JudgeOnline/problem.php?id=1040
题解
1A二连击!
题目显然给了一个基环内向树森林,说是一个点和它指向的点中只能选择一个,你要安排选择哪些点,使权值和最大。
显然可以转成无向环套树,因为原来的图就等价于相邻的两个点只能选择其一。
然后就可以找到环,随便找环上两个点u和v,断掉一条连边,然后分别强制不选u和v,做树形dp,ans取最大值就好了。
注意是森林!
还有个地方,就是可能出现两个点之间有两条边的情况,因此断边的时候应该记录边的序号,而不是记录点对序号。
代码
//环套树、动态规划
#include <cstdio>
#include <algorithm>
#define maxn 1000002
#define ll long long
using namespace std;
int head[maxn], to[maxn<<1], nex[maxn<<1], N, u, v, cut, tot=1;
ll f[maxn][2], w[maxn];
bool vis[maxn];
void adde(ll a, ll b){to[++tot]=b;nex[tot]=head[a];head[a]=tot;}
int read(int x=0)
{
char c=getchar();
while(c<48 or c>57)c=getchar();
while(c>=48 and c<=57)x=(x<<1)+(x<<3)+c-48,c=getchar();
return x;
}
void input()
{
int i, x;
N=read();
for(i=1;i<=N;i++)
{
w[i]=read();
x=read();
adde(i,x), adde(x,i);
}
}
void dp(int pos, int fa)
{
int p;
f[pos][1]=w[pos];
for(p=head[pos];p;p=nex[p])
if(to[p]!=fa and p!=cut and p!=(cut^1))
{
dp(to[p],pos);
f[pos][0]+=max(f[to[p]][0],f[to[p]][1]);
f[pos][1]+=f[to[p]][0];
}
}
void dfs(int pos, int pre)
{
int p;
vis[pos]=1;
for(p=head[pos];p;p=nex[p])
if(p!=(pre^1))
{
if(!vis[to[p]])dfs(to[p],p);
else u=pos, v=to[p];
}
}
ll work(int pos)
{
int i, p; ll ans=0;
u=v=0;dfs(pos,0);
for(p=head[u];to[p]!=v;p=nex[p]);cut=p;
dp(u,0);ans=f[u][0];
for(i=1;i<=N;i++)f[i][0]=f[i][1]=0;
dp(v,0);ans=max(ans,f[v][0]);
return ans;
}
int main()
{
int i; ll ans=0;
input();
for(i=1;i<=N;i++)if(!vis[i])ans+=work(i);
printf("%lld",ans);
return 0;
}