[學習筆記] 基環樹
阿新 • • 發佈:2021-10-29
概念
具有\(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; }