题意
给一个基环森林,每个点有一个权值
求一个点的集合,使集合里面不存在相连的两个点
思路
基环树: 树加一条边使之成环
如果是树就正常树形dp
f[i][0]=
Σ{max(f[son(i)][0],f[son(i)][1])}
Σ
{
m
a
x
(
f
[
s
o
n
(
i
)
]
[
0
]
,
f
[
s
o
n
(
i
)
]
[
1
]
)
}
f[i][1]=
Σ{f[son(i)][0]}
Σ
{
f
[
s
o
n
(
i
)
]
[
0
]
}
现在有一条环边
dfs找到环边
从该边的两个端点出发选择,
1) 强制不选U,V任意,环的贡献为以U做DP的f[U][0]
2) 强制不选V,U任意,环的贡献为以V做DP的f[V][0]
代码
#include <algorithm>
#include <bits/stdc++.h>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
const int INF = 0x7f7f7f7f;
const int maxn = 1e6 + 10;
int n;
ll val[ maxn ];
ll dp[ maxn ][ 2 ];
bool vis[ maxn ];
bool visdp[ maxn ];
struct EDGE {
int to, nex, id;
} edg[ 2 * maxn ];
int cnt;
ll sol;
int head[ maxn ];
void INit () {
cnt = 0, sol = 0;
memset ( dp, 0, sizeof ( dp ) );
memset ( vis, false, sizeof ( vis ) );
memset ( visdp, false, sizeof ( visdp ) );
memset ( head, -1, sizeof ( head ) );
memset ( edg, 0, sizeof ( edg ) );
}
void AddEdge ( int u, int v ) {
edg[ cnt ].to = v;
edg[ cnt ].nex = head[ u ];
head[ u ] = cnt++;
}
int U, V;
void dfs ( int rt, int fa ) {
//遍历森林
vis[ rt ] = true;
for ( int i = head[ rt ]; i != -1; i = edg[ i ].nex ) {
int v = edg[ i ].to;
if ( v == fa )
continue;
if ( vis[ v ] ) {
//在一个子树上找到环边
U = rt, V = v;
continue;
}
//遍历完整个子树
dfs ( v, rt );
}
}
bool fg;
void tree_dp ( int rt ) {
dp[ rt ][ 1 ] = val[ rt ];
dp[ rt ][ 0 ] = 0;
visdp[ rt ] = true;
for ( int i = head[ rt ]; i != -1; i = edg[ i ].nex ) {
int v = edg[ i ].to;
if ( visdp[ v ] )
continue;
/*
* 还是用vis数组比较好用,fa在这样的环里容易循环
* 不过用fa的话还是不对
//if ( i == ban || ( i ^ 1 ) == ban )
//if ( ( rt == U && v == V ) || ( rt == V && v == U ) )
if ( fg && v == U ) //从U开始时,V到U的边不能选
continue;
if ( !fg && v == V ) //从V开始时,V可以到U,U到V的边不能选
continue;
*/
tree_dp ( v );
dp[ rt ][ 0 ] += max ( dp[ v ][ 0 ], dp[ v ][ 1 ] );
dp[ rt ][ 1 ] += dp[ v ][ 0 ];
}
}
int main () {
#ifdef LOCAL
freopen ( "in", "r", stdin );
// freopen("out","w",stdout);
#endif
while ( ~scanf ( "%d", &n ) ) {
INit ();
for ( int i = 1, v; i <= n; ++i ) {
scanf ( "%lld%d", &val[ i ], &v );
AddEdge ( i, v );
AddEdge ( v, i );
}
for ( int i = 1; i <= n; ++i ) {
//森林
if ( !vis[ i ] ) {
dfs ( i, -1 );
// fg没用
//从U出发,U强制不选
fg = true;
memset ( visdp, false, sizeof ( visdp ) );
tree_dp ( U );
ll tmp = dp[ U ][ 0 ];
fg = false;
memset ( visdp, false, sizeof ( visdp ) );
tree_dp ( V );
tmp = max ( tmp, dp[ V ][ 0 ] );
//该子树最大的值
sol += tmp;
}
}
printf ( "%lld\n", sol );
}
return 0;
}