日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

基环树小记

發布時間:2023/12/3 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基环树小记 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

概念

基環樹就是有n個點n條邊的圖(比樹多出現一個環)。

特殊形態的基環樹


無向樹(N點N邊無向圖)


外向樹(每個點只有一條入邊)


內向樹(每個點只有一條出邊)

以上三種樹有十分優秀的性質,就是可以直接將環作為根。就可以對每個環的子樹進行單獨處理,最后再處理環

找環

  • 無向圖
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=5010; struct Edge{int v,nxt;}edge[N<<1]; struct Line{int u,v;}line[N<<1]; int n,m,cnt,head[N],dfn[N],ind,fa[N],loop[N],len; bool ban[N][N]; int t,ans[N],rec[N]; bool cmp(Line a,Line b){return a.v>b.v;} void addedge(int u,int v){edge[++cnt].v=v;edge[cnt].nxt=head[u];head[u]=cnt; } void getloop(int u){dfn[u]=++ind;for(int i=head[u];i;i=edge[i].nxt){int v=edge[i].v;if(v==fa[u]) continue;if(dfn[v]){if(dfn[v]<dfn[u]) continue;for(int x=v;x!=fa[u];x=fa[x]) loop[++len]=x;}else{fa[v]=u;getloop(v);}} } void dfs(int u,int f){rec[++t]=u;for(int i=head[u];i;i=edge[i].nxt){int v=edge[i].v;if(v==f) continue;if(ban[u][v]) continue;dfs(v,u);} } void getmin(){bool flag=0;int i;for(i=1;i<=n;i++){if(rec[i]<ans[i]){flag=1;break;}else if(rec[i]>ans[i]) return;}if(!flag) return;for(;i<=n;i++) ans[i]=rec[i]; } int main(){memset(ans,0x7f,sizeof(ans));scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){int u,v;scanf("%d%d",&u,&v);line[i]=(Line){u,v};line[i+m]=(Line){v,u};}sort(line+1,line+2*m+1,cmp);for(int i=1;i<=2*m;i++) addedge(line[i].u,line[i].v);if(m==n-1){dfs(1,0);for(int i=1;i<=n;i++) printf("%d ",rec[i]);return 0;}getloop(1);loop[++len]=loop[1];for(int i=1;i<len;i++){//枚舉刪除的邊ban[loop[i]][loop[i+1]]=ban[loop[i+1]][loop[i]]=1;t=0;dfs(1,0);getmin();ban[loop[i]][loop[i+1]]=ban[loop[i+1]][loop[i]]=0;}for(int i=1;i<=n;i++) printf("%d ",ans[i]);return 0; }
  • 有向圖
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #define N 1000010 #define int long long using namespace std; struct Edge{int v,nxt;}edge[N]; int w[N],n,ans,cnt,f[N][2],head[N],d[N],mark; bool vis[N]; void add(int u,int v){edge[++cnt].v=v;edge[cnt].nxt=head[u];head[u]=cnt; } void getloop(int u){//找環vis[u]=true;if(vis[d[u]]) mark=u;else getloop(d[u]);return; } void dp(int u){vis[u]=true;f[u][1]=w[u];f[u][0]=0;for(int i=head[u];i;i=edge[i].nxt){int v=edge[i].v;if(v!=mark){dp(v);f[u][0]+=max(f[v][1],f[v][0]);f[u][1]+=f[v][0];}else f[v][1]=-2147483647/3;}return; } signed main(){scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d%d",&w[i],&d[i]);add(d[i],i);}for(int i=1;i<=n;i++){if(vis[i]) continue;getloop(i);dp(mark);int maxn=max(f[mark][0],f[mark][1]);mark=d[mark];dp(mark);ans+=max(maxn,max(f[mark][0],f[mark][1]));}printf("%lld",ans); }

一般問題解決方法

一般解決方法都是如果是樹形dp,就對環部分進行環形dp的操作。不然可以考慮斷環法。

  • 斷環法
    每次斷開環上的一條邊跑一遍答案,然后取最大值。
    適用于:數據較小,且環不會影響答案的題目
    [NOIP2018 提高組] 旅行
  • 二次dp法
    對于環,我們可以像環形dp一樣將一條邊強行斷開處理一遍,然后強行連上再處理一遍
    適用于:這樣子跑滿足正確性的
    [ZJOI2008]騎士
  • 斷環復制法
    對于環,我們可以像環形dp一樣斷開環,然后再復制一遍放在后面
    適用于:多個需要維護的
    [IOI2008] Island

總結

以上是生活随笔為你收集整理的基环树小记的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。