BZOJ1040
每一个人会痛恨一个人,将痛恨的那个人设为父节点,显然每一个人只有一个入度。
分析(画图)可知,这个题目的模型是一个基环树森林。
我们只需要随便在基环树的环上取一个边,对边的两边的节点分别树形dp。
(0) 对于一个边,他有两个点,这两个点不能同时取。
(1) 对于一个边,一个点为x,一个点为fa[x]。
(2) 首先我们强制不选x,进行树形dp。
(3) 然后我们强制不选fa[x],再树形dp一遍。
然后比较一下,取一个最大值加起来即可。
状态转移如下:
对于一条边u->v
dp[u][0] += max(dp[v][0],dp[v][1])
dp[u][1] += dp[v][0]
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 2e6+7;
int fa[maxn],val[maxn],vis[maxn];
ll dp[maxn][2],ans = 0;
vector<int> G[maxn];
int root;
void dfs(int now)
{
vis[now] = 1;
dp[now][0] = 0,dp[now][1] = val[now];
for(int i=0;i<G[now].size();i++)
{
int v = G[now][i];
if(v!=root)
{
dfs(v);
dp[now][0] += max(dp[v][0],dp[v][1]);
dp[now][1] += dp[v][0];
}
else dp[root][1] = -INF;
}
}
void findc(int x)
{
vis[x] = 1;
root = x;
while(!vis[fa[root]])
{
root = fa[root];
vis[root] = 1;
}
dfs(root);
long long tmp = max(dp[root][0],dp[root][1]);
root = fa[root];
dfs(root);
ans += max(tmp,max(dp[root][0],dp[root][1]));
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int x;
scanf("%d%d",val+i,&x);
fa[i] = x;
G[x].push_back(i);
}
for(int i=1;i<=n;i++)
{
if(!vis[i]) findc(i);
}
printf("%lld\n",ans);
return 0;
}