[NOIP2017模拟]建设图

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;
}

本题结。

发布了152 篇原创文章 · 获赞 136 · 访问量 5万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章