【算法学习】基环树
基环树
基环树就是类似于在树上加了一条边形成了环,去点环上的一条边后就会变成数,如下图。
这是一个 \(n\) 个点 \(n\) 条边的连通图,如果不保证联通,它就会成为基环树森林。
外向树:每个点都只有一条入边,因为向内上。
内向树:每个点都只有一条出边,因为向外少。
怎么用呢?
1.把环取出,先对树操作,再对环操作,但是这样明显有时会很复杂。
2.把环上一条边忽视掉,在这条边的两个端点分别操作整棵树。
例题
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;
}
P1453 城市环路
我是看不懂题一点,直接看大佬解释题意了,给出一个 \(N\) 个点 \(N\) 条边的连通图,每个点有点权,求出其最大独立集(不能选取相邻的节点情况下能选取点集的最大点权和)
发现这与上面那题一模一样,没啥意思直接秒掉!!!
如何找到环边可以用并查集找到如果两个点在同一连通块内就说明该点为环边,然后就可以树形dp了,所有为什么我调了15分钟呢???
#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];
double f[N][3];
double fa[N];
double k;
double a[N];
double ans=0;
double mx=0;
int root=1;
void dfs(int x,int faa){
vis[x]=1;
f[x][0]=0;
f[x][1]=a[x];
f[root][1]=-inf;
for(int i:v[x]){
if(i==faa){
continue;
}
if(i!=root&&!vis[i]){
dfs(i,x);
f[x][0]+=max(f[i][1],f[i][0]);
f[x][1]+=f[i][0];
}
}
}
int l,r;
int find(int x){
return fa[x]==x?x:fa[x]=find(fa[x]);
}
signed main(){
// ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++){
fa[i]=i;
}
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
int u,vv;
cin>>u>>vv;
u++;
vv++;
v[u].push_back(vv);
v[vv].push_back(u);
if(find(u)==find(vv)){
l=u,r=vv;
}
else{
u=find(u);
vv=find(vv);
fa[u]=vv;
}
}
cin>>k;
mx=0;
root=l;
dfs(root,0);
mx=max(f[l][1],f[l][0]);
memset(f,0,sizeof f);
memset(vis,0,sizeof vis);
root=r;
dfs(root,0);
ans=max(mx,max(f[r][1],f[r][0]));
printf("%.1lf",ans*k);
return 0;
}