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;
}
本题结。