您的位置:首页 > 其它

[NOIP2017模拟]建设图

2017-10-24 15:56 330 查看
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……(其实还少了一个特判,如果整个图都是双连
4000
通的,那最后就只有一个根点,会被判成叶子节点,答案就是(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;
}


本题结。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: