分析图的特点可以发现,该图是由n个点n条边构成,并且可以由多个连通块构成,每个连通块最多只有一个环。
所以图上点的情况只有两种:在环上或不在环上
对于不在环上的点可以直接使用树形动态规划求解。
对于在环上的点,只需要分两种情况:选了第一个点,或者选了最后一个点,取一个最大值就行。
动态规划状态:f[i][0],f[i][1]分别表示选了第i个点和不选第i个点的最大武力值。
转移方程:f[i][0] = max{ f[k][0], f[k][1]},f[i][1] = max{ f[k][0] } + w[i],其中k是i的子节点
由于n的范围较大,使用递归可能爆系统栈,需要模拟堆栈来实现,同时需要使用long long保存答案。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 1000000 + 10;
int w[maxn],hate[maxn],ind[maxn];
int stack[maxn],circle[maxn];
long long f[maxn][2];
int n,top = 0;
void init()
{
freopen("bzoj1040.in","r",stdin);
freopen("bzoj1040.out","w",stdout);
}
void readdata()
{
scanf("%d",&n);
for(int i = 1;i <= n;i++)
{
scanf("%d%d",&w[i],&hate[i]);
++ind[hate[i]];
}
}
void solve()
{
for(int i = 1;i <= n;i++)
{
if(!ind[i])stack[top++] = i;
}
while(top)
{
int u = stack[--top],v = hate[u];
f[u][1] += w[u];f[v][1] += f[u][0];
f[v][0] += max(f[u][0],f[u][1]);
--ind[v];
if(!ind[v])stack[top++] = v;
}
long long ans = 0;
for(int i = 1;i <= n;i++)
{
if(ind[i])
{
int len = 0,u = i;
do
{
f[u][1] += w[u];
circle[len++] = u;
ind[u] = 0;
u = hate[u];
}while(ind[u]);
long long x = f[circle[len-1]][0],y = 0;
for(int j = 0;j < len - 1;j++)
{
int u = circle[j];
long long nx = max(x,y) + f[u][0],ny = x + f[u][1];
x = nx,y = ny;
}
long long dropN = max(x,y);
x = f[circle[0]][0];y = 0;
for(int j = 1;j < len;j++)
{
int u = circle[j];
long long nx = max(x,y) + f[u][0],ny = x + f[u][1];
x = nx,y = ny;
}
long long drop1 = max(x,y);
ans += max(drop1,dropN);
}
}
printf("%lld\n",ans);
}
int main()
{
init();
readdata();
solve();
return 0;
}