luogu題解 P3388 【【模板】割點(割頂)】
阿新 • • 發佈:2019-02-01
題解 搜索樹 cpp 沒有 head 判斷 algo 以及 clu
外加定義:在一個無向圖中,如果刪掉點 x 後圖的連通塊數量增加,則稱點 x 為圖的割點。
外加圖示
開始思路為割橋上的點為割點,後來證明的確正確。
不過可惜的是他的逆定理錯了(gg了),不過數據很弱以至於得了90分。
如圖所示
圖中無割橋,但點3卻是割點,貌似無法解決。
(順及客串my blog
,以及圖論的必要工具
回歸正題,另一種思路誕生了:
如果u點的子節點為v,v點他能返回的最老祖先比u點年輕或一樣(即dfn[u]值<=low[v]),那麽如果刪去u點,那麽v以下的點就會與v以上的點失去聯系,就會產生新的連通塊(實質是在我的原來思路上多了一個判斷
也就是說如果在我們的搜索樹上有一個點只有樹邊與祖先相連,而沒有反向邊連回祖先節點的話,那麽它就是割點。就是沒有這樣的邊
至於實現方法貌似蒟蒻所知只有Tarjan。
這道題是模板題,大家還是不要抄代碼為好。(事關今後的Tarjan生涯)
代碼
#include<cstdio> #include<algorithm> #include<stack> #include<cstring> #define Max 1000000+199 using namespace std; int n,m,dfn[Max]={0},low[Max],cast[Max],ins[Max],inx=0,head[Max],v[Max]={0},cnt=0,gs=0,cd[Max]={0}; stack<int> s; struct edge { int c,to,next; }e[Max]; void adde(int a,int b) { cnt++; e[cnt].to=b; e[cnt].c=a; e[cnt].next=head[a]; head[a]=cnt; cd[a]++; } int ans=0,gd[Max]={0}; void tarjan(int x,int fa) { int u,sk=0; inx++; dfn[x]=low[x]=inx; s.push(x); ins[x]=1; for(int i=head[x];~i;i=e[i].next) { u=e[i].to; if(dfn[u]==0) { tarjan(u,fa); if(low[u]>=dfn[x]&&x!=fa)gd[x]=1; v[i]=v[i%2==0?i-1:i+1]=1; low[x]=min(low[x],low[u]); if(x==fa)sk++; } else if(ins[u]==1&&v[i]==0)v[i]=v[i%2==0?i-1:i+1]=1,low[x]=min(low[x],dfn[u]); } if(dfn[x]==low[x]) { gs++; u=Max; while(u!=x) { u=s.top(); s.pop(); ins[u]=0; cast[u]=gs; //printf("%d %d\n",u,gs); } } if(x==fa&&sk>=2)gd[x]=1; } int main() { memset(cd,0,sizeof(cd)); memset(head,-1,sizeof(head)); scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int a,b; scanf("%d%d",&a,&b); adde(a,b); adde(b,a); //printf("%d",v[i]); } for(int i=1;i<=n;i++) if(dfn[i]==0)tarjan(i,i); for(int i=1;i<=n;i++) { if(gd[i]==1)ans++; } printf("%d\n",ans); for(int i=1;i<=n;i++) { if(gd[i]==1)printf("%d ",i); } return 0; }
luogu題解 P3388 【【模板】割點(割頂)】