1. 程式人生 > >2018.09.23 關鍵網線(tarjan)

2018.09.23 關鍵網線(tarjan)

描述

給出一個無向連通圖,即在任一個點對間存在路徑。有的點提供服務a, 有的點提供服務b 。同一個點可能有兩種服務型別。每個點必須與提供2種服務的點連通。如果一個邊斷掉,就可能出現有些點不能被服務到,那麼這條邊就稱為關鍵邊。你的任務是找出關鍵邊數量以及每條關鍵邊。

輸入

第一行是整數N,M,K,L (1<=N<=100000, 1<=M<=1000000, 1<=K<=N,1<=L<=N)。N是圖節點數;M是邊數;k是提供服務a的點個數;L是提供服務b的點個數。第二行有K個數,每個數表示提供服務a的節點。第三行有L個數,每個數表示提供服務b的節點。接下來M行,每行兩個不同的數,他們表示一條邊的兩個節點。

輸出

輸出檔案只有一行為一個整數S,表示關鍵邊的數量。

樣例輸入

9 10 3 4 2 4 5 4 9 8 3 1 2 4 1 2 3 4 2 1 5 5 6 6 7 6 8 7 9 8 7

樣例輸出

3

標籤

ceoi2005

對於每一個連通分量記錄其中有幾個a類點和幾個b類點。 然後對於每一條割邊分成的兩個連通分量判一下是不是有沒有a類點或者沒有b類點的就行了。 程式碼:

#include<bits/stdc++.h>
#define N 100005
#define M 1000005
using namespace std;
int first[N],dfn[N],low[N],dfs_blocks,
n,m,k,l,cnt=0,cnta[N],cntb[N],ans=0; struct edge{int v,next;}e[M<<1]; inline int read(){ int ans=0; char ch=getchar(); while(!isdigit(ch))ch=getchar(); while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar(); return ans; } inline void add(int u,int v){e[++cnt].v=v,e[cnt].next=
first[u],first[u]=cnt;} inline int min(int a,int b){return a<b?a:b;} inline void swap(int&a,int&b){a^=b,b^=a,a^=b;} inline void tarjan(int p,int fa){ dfn[p]=low[p]=++dfs_blocks; for(int i=first[p];i;i=e[i].next){ int v=e[i].v; if(v==fa)continue; if(dfn[v]){low[p]=min(low[p],low[v]);continue;} tarjan(v,p),low[p]=min(low[p],low[v]); if(low[v]>dfn[p])if(!cnta[v]||!cntb[v]||cnta[v]==k||cntb[v]==l)++ans; cnta[p]+=cnta[v],cntb[p]+=cntb[v]; } } int main(){ n=read(),m=read(),k=read(),l=read(); for(int i=1;i<=k;++i)cnta[read()]=1; for(int i=1;i<=l;++i)cntb[read()]=1; for(int i=1;i<=m;++i){ int u=read(),v=read(); add(u,v),add(v,u); } tarjan(1,0); cout<<ans; return 0; }