2017.10.24 T1 2007
樣例數據
輸入
7 7
1 2
2 3
3 4
2 5
4 5
5 6
5 7
輸出
2
分析:看到這道題我就知道它是個tarjan了,只是不知道求什麼,看樣例我以爲是求割點,出其他數據發現不對;然後覺得也許是割邊,發現也不對;想了半天之後,覺得tarjan剩下的就只有雙連通分量了,試了試縮點,嗯,變成了一棵樹,於是我就又建了幾棵樹,發現需要(葉子節點+1)/2條邊就可以把整棵樹變成雙連通圖(根節點如果只連了一條邊也算是葉子),然後興沖沖地打好了這道題,還檢查了很久,覺得這道題就是今天的得分支柱了。
然而,今天在linux下評測,我的tarjan多年來記dfn和low值的都是“index”,在linux中“index”是關鍵字,直接編譯錯誤,爆0……而在oj上的評測機是windows環境,AC……(其實還少了一個特判,如果整個圖都是雙連通的,那最後就只有一個根點,會被判成葉子節點,答案就是(1+1)/2=1,實際上並不需要連邊,但是不黑心的wuvin並沒有出這樣的數據卡我嘿嘿嘿)
結論:一棵樹要加邊成爲雙連通圖需要加(葉子節點數+1)/2條邊(如果根節點只連了一條邊也算作葉子節點)。
代碼
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#include<queue>
#include<set>
using namespace std;
int getint()
{
int sum=0,f=1;
char ch;
for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
if(ch=='-')
{
f=-1;
ch=getchar();
}
for(;isdigit(ch);ch=getchar())
sum=(sum<<3)+(sum<<1)+ch-48;
return sum*f;
}
const int maxn=100010;
const int maxm=200010;
int n,m,ans;
int tot,first[maxn],nxt[maxm*2],to[maxm*2];
int dep,dfn[maxn],low[maxn],zhan[maxn],top,cnt,slt[maxn];
int num,chudu[maxn];
bool visit[maxn];
void addedge(int x,int y)
{
tot++;
nxt[tot]=first[x];
first[x]=tot;
to[tot]=y;
tot++;
nxt[tot]=first[y];
first[y]=tot;
to[tot]=x;
}
void tarjan(int u,int fro)
{
dep++;//千萬不要再用index了!!!
dfn[u]=dep;
low[u]=dep;
zhan[top]=u;
top++;
for(int p=first[u];p;p=nxt[p])
{
int v=to[p];
if(!dfn[v])
{
tarjan(v,p);
low[u]=min(low[u],low[v]);
}
else
if((p^1)!=fro)
low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u])//把雙連通分量縮點
{
cnt++;
while(zhan[top]!=u)
{
top--;
slt[zhan[top]]=cnt;
}
}
}
void dfs(int u)
{
for(int p=first[u];p;p=nxt[p])
{
int v=to[p];
if(!visit[v])
{
visit[v]=true;
if(slt[v]!=slt[u])//不是同一個連通塊所以u的連通塊的出度++
chudu[slt[u]]++;
dfs(v);
}
}
}
int main()
{
freopen("graph.in","r",stdin);
freopen("graph.out","w",stdout);
int x,y;tot=1;
n=getint(),m=getint();
for(int i=1;i<=m;++i)
{
x=getint(),y=getint();
addedge(x,y);
}
tarjan(1,0);//tarjan求雙連通分量並縮點
visit[1]=true;
dfs(1);//從根開始向深處搜,找到出度爲0的點,也就是葉子節點
for(int i=1;i<=cnt;++i)
if(chudu[i]==0)
num++;
if(chudu[slt[1]]==1)//特判一下根節點是不是隻連了一條邊
num++;
if(num==1)//整個圖就只有一個點的話
ans=0;
else
ans=(num+1)/2;//結論
cout<<ans<<'\n';
return 0;
}
本題結。