1. 程式人生 > 其它 >[學習筆記] 基環樹

[學習筆記] 基環樹

概念

具有\(N\)個點\(N\)條邊的連通圖,如果圖不是連通的,就會變成基環樹森林

除此之外,還有內向樹:每個點有且只有一條出邊,外向樹:每個點有且只有一條入邊

典型套路

一般有:基環樹直徑,基環樹兩點間的距離,基環數DP等型別的題目

一般做法用:

  1. 斷環
  2. 把環和剩下的邊分開處理
例題

P1453 城市環路

由於是無向圖,所以拓撲找環的時候判要是不是為\(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;
}