zoukankan      html  css  js  c++  java
  • Blockade(Bzoj1123)

    Description

    Byteotia城市有n个 towns m条双向roads. 每条 road 连接 两个不同的 towns ,没有重复的road. 所有towns连通。

    Input

    输入n<=100000 m<=500000及m条边

    Output

    输出n个数,代表如果把第i个点去掉,将有多少对点不能互通。

    Sample Input

    5 5
    1 2
    2 3
    1 3
    3 4
    4 5

    Sample Output

    8
    8
    16
    14
    8

     

    【题前话】

        这题真的害人不浅啊!

        本蒟蒻怀着做板子题1A的心态打开这道题,结果经过了三个多小时,经历了又A又RE又WA又栈溢出的代码,终于在机房大佬的题解和讲解之下终于AC了,RE和栈溢出的原因竟是因为一个变量开了long long!必须定义时定义int,乘的时候改为long long才能A!

        本蒟蒻都惊到了,果然计算机是一个玄学的学科,对萌新真的很不友好!我到现在还是不明白,如果有路过大佬能解答我的疑惑的,在下感激不尽!(具体是哪个变量我会在题外话里写出)

     

    【题解】

        首先,大家应该都知道,这道题是用TARJAN算法的。(没学过的小盆友可以去学习一发:https://www.cnblogs.com/mxrmxr/p/9715579.html)

    有的人或许觉得这道题是TARJAN缩点,其实不然。这道题是用TARJAN求割点的。具体思路如下:

        打一个TARJAN模板,按照DFS树搜索,DFS返回后,判断当前点是不是割点,如果是的话,那么这个点和刚才搜索的点就是一个联通块。

    那我们怎么处理有多少对点呢?

        其实很简单,就是乘法原理,相信大家都学过吧。(没学过的出门右转去语文竞赛吧

        总体思路如下:

        对于每个割点,它的儿子们肯定有至少两个联通块,如果这个割点被删除,下面的联通块都不会互通。我们定义ans[u]为这个点去掉后有多少点不能互通,我们在计算每个它下面的联通块时,把它们的大小依次乘起来(就是下面的核心代码,这里看不懂就先往下翻)。然后,再加上它儿子们的联通块的和乘以他父亲们所有点的和,(就是乘法原理啦)这样,我们的思路就基本结束了。

        上文的核心代码是怎么写的呢?如下:

    if(low[v]>=dfn[u])
    {
        ans[u]+=(ll)sum*size[v];
        sum+=size[v];
    }

        我们定义size代表以u为根的联通块大小,sum表示已经遍历的子联通块,当我们发现一个新的联通块时,要把sum的大小乘以已经遍历的(乘法原理),就是size[v]啦。这时我们又遍历了一个,所以sum要加上它。(就是size[v] 啦)

        总代码如下:

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <cstdlib>
    using namespace std;
    #define MAXN 1000010
    #define num ch-'0'
    #define ll long long
    void get(int &res)
    {
        char ch;bool flag=0;
        while(!isdigit(ch=getchar()))
            (ch=='-')&&(flag=true);
        for(res=num;isdigit(ch=getchar());res=res*10+num);
        (flag)&&(res=-res);
    }
    int n,m,tot=0,cnt;
    int hea[MAXN],nex[MAXN<<4],to[MAXN<<4];
    int dfn[MAXN],low[MAXN],size[MAXN];
    ll ans[MAXN<<2];
    inline void add(int a,int b)
    {
        to[++tot]=b;
        nex[tot]=hea[a];
        hea[a]=tot;
    }    
    inline void tarjan(int u)
    {
        int sum=0;
        dfn[u]=low[u]=++cnt;
        size[u]=1;
        for(int i=hea[u];i;i=nex[i])
        {
            int v=to[i];
            if(!dfn[v])
            {
                tarjan(v);
                size[u]+=size[v];
                low[u]=min(low[u],low[v]);
                if(low[v]>=dfn[u])
                {
                    ans[u]+=(ll)sum*size[v];
                    sum+=size[v];
                }
            }
            else low[u]=min(low[u],dfn[v]);
        }
        ans[u]+=(ll)sum*(n-sum-1);
    }
    int main()
    {
        get(n),get(m);
        for(int i=1;i<=m;i++)
        {
            int a,b;
            get(a),get(b);
            add(a,b);add(b,a);
        }
        tarjan(1);
        for(int i=1;i<=n;i++)
          printf("%lld
    ",(ans[i]+n-1)<<1);
    }

    【题外话】

        在写TARJAN时,我的sum(含义如上文所讲)如果int就会WA,如果long long就会RE和栈溢出,只有先定义为int,后面乘法时改为long long才能A。

        本蒟蒻十分不解,还望请过路大神指点高明。

  • 相关阅读:
    第十二章学习笔记
    UVa OJ 107 The Cat in the Hat (戴帽子的猫)
    UVa OJ 123 Searching Quickly (快速查找)
    UVa OJ 119 Greedy Gift Givers (贪婪的送礼者)
    UVa OJ 113 Power of Cryptography (密文的乘方)
    UVa OJ 112 Tree Summing (树的求和)
    UVa OJ 641 Do the Untwist (解密工作)
    UVa OJ 105 The Skyline Problem (地平线问题)
    UVa OJ 100 The 3n + 1 problem (3n + 1问题)
    UVa OJ 121 Pipe Fitters (装管子)
  • 原文地址:https://www.cnblogs.com/mxrmxr/p/9720857.html
Copyright © 2011-2022 走看看