2018.09.23 關鍵網線(tarjan)
阿新 • • 發佈:2018-12-11
描述
給出一個無向連通圖,即在任一個點對間存在路徑。有的點提供服務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;
}