[学习笔记] 基环树
概念
具有\(N\)个点\(N\)条边的连通图,如果图不是连通的,就会变成基环树森林
除此之外,还有内向树:每个点有且只有一条出边,外向树:每个点有且只有一条入边
典型套路
一般有:基环树直径,基环树两点间的距离,基环数DP等类型的题目
一般做法用:
- 断环
- 把环和剩下的边分开处理
例题
由于是无向图,所以拓扑找环的时候判要是不是为\(1\),而不是\(0\)
然后拆环跑树形DP就行了
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <ctime>
#include <queue>
#define int long long
using namespace std;
const int N=2e6+10;
const int maxn=1e9;
int tot,head[N],nxt[N],n,m,to[N],dist[N],ver[N];
bool vis[N],flag;
queue<int> q;
int in[N];
double k,val[N],dp[N][4],ans;
int fa[N],root;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
inline void add(int x,int y){
ver[++tot]=y,nxt[tot]=head[x],head[x]=tot;
}
void treedp(int u,int fa,int no){
dp[u][1]=val[u];
dp[u][0]=0;
for(int i=head[u];i;i=nxt[i]){
int v=ver[i];
if(v==fa) continue;
if(v!=no){
treedp(v,u,no);
dp[u][1]+=dp[v][0];
dp[u][0]+=max(dp[v][0],dp[v][1]);
}
}
}
void topo(){
for(int i=1;i<=n;i++)
if(in[i]==1) q.push(i);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
--in[y];
if(in[y]==1) q.push(y);
}
}
}
void treedp(int u,int fa,int no_u,int no_v){
dp[u][0]=0,dp[u][1]=val[u];
for(int i=head[u];i;i=nxt[i]){
int v=ver[i];
if(v==fa) continue;
if(v==no_v&&u==no_u) continue;
if(v==no_u&&u==no_v) continue;
treedp(v,u,no_u,no_v);
dp[u][0]+=max(dp[v][1],dp[v][0]);
dp[u][1]+=dp[v][0];
}
}
void findcir(int u){
for(int i=head[u];i;i=nxt[i]){
int v=ver[i];
if(in[v]>1){
treedp(u,v,u,v);ans=dp[u][0];
treedp(v,u,v,u);ans=max(ans,dp[v][0]);
break;
}
}
printf("%0.1lf",ans*k);
}
signed main(){
n=read();
for(int i=1;i<=n;++i) val[i]=read();
for(int i=1;i<=n;++i){
int x=read(),y=read();
++x,++y;
add(x,y);
add(y,x);
++in[x],++in[y];
}
scanf("%lf",&k);
topo();
for(int i=1;i<=n;i++){
if(in[i]>1){
findcir(i);
return 0;
}
}
return 0;
}

浙公网安备 33010602011771号