基环树
基环树就是类似于在树上加了一条边形成了环,去点环上的一条边后就会变成数,如下图。
这是一个 \(n\) 个点 \(n\) 条边的连通图,如果不保证联通,它就会成为基环树森林。
外向树:每个点都只有一条入边,因为向内上。
内向树:每个点都只有一条出边,因为向外少。
怎么用呢?
因为有环的性质,所以可以将环拆成树,再对树继续树形dp即可。
例题
P2607 [ZJOI2008] 骑士
因为一个骑士只会讨厌另外一个骑士,所以每个连通块都是一颗基环树,所以我们对每颗基环树拆环成树进行树形dp即可。
我们可以把图建成一个有向图,他讨厌的骑士作他的父亲,因为我们环上相邻的两个点都不能选,所以我们先强制不选根节点进行一遍树形dp,状态为f[x][1/0]表示选/不选x节点最大的权值,然后再对根节点的父亲进行一遍树形dp,这样就包含了所有情况了!!
#include <bits/stdc++.h>
#define re register
const int N=1e6+1e5;
#define int long long
const int inf=1e9;
using namespace std;
int n;
vector<int> v[N];
int vis[N];
int f[N][3];
int fa[N];
int a[N];
int ans=0;
int mx=0;
int root=1;void dfs(int x){vis[x]=1;f[x][0]=0;f[x][1]=a[x];for(int i:v[x]){if(i!=root){dfs(i);f[x][0]+=max(f[i][1],f[i][0]);f[x][1]+=f[i][0];}else{f[i][1]=-inf;}}
} signed main(){ios::sync_with_stdio(false);cin>>n;for(int i=1;i<=n;i++){cin>>a[i]>>fa[i];v[fa[i]].push_back(i);}for(int i=1;i<=n;i++){if(!vis[i]){vis[i]=1;mx=0;root=i;while(!vis[fa[root]]){vis[root]=1;root=fa[root];}vis[root]=1;dfs(root);mx=max(f[root][1],f[root][0]);root=fa[root];dfs(root);ans+=max(mx,max(f[root][1],f[root][0]));}}cout<<ans;return 0;
}