【图论】Ural 1557

来源:互联网 发布:域名真实ip 编辑:程序博客网 时间:2024/06/06 14:04

Ural 1557  <---原题连接


题目大意为,两个黑客攻击一个相互连通的网络。网络中有n台计算机和m条链路。每一个人可以选择一条链路进行攻击并将其截断,问在这两个人攻击完成后,能够将这个网络分割成至少两个部分的方案由多少种。

需要注意的是,两名黑客不会攻击同一条链路。这也就说明,如果网络中仅有两个节点和一条边,答案应该是0(黑客不会同时攻击这一条链路,所以没法完成任务)

数据范围:

1 ≤ N ≤ 2000; 

0 ≤ M ≤ 100000;

首先,如果攻击的两条边中有一条是“桥”,那么剩下的一条边可以任意选择。因此,我们将这个图中的所有桥的数量求出,记为bridge,那么所有选择中,有桥的方案总数应该为:  bridge*(m-bridge)+bridge*(bridge-1) / 2(即是一条桥和一条非桥的边组成的方案数 + 两条都是桥组成的方案数。)


接下来,我们需要找到两条非桥的边组成的所有方案。具体方法为:

对图进行一遍dfs,得到图的dfs树。

若i为dfs树中的一个节点:

我们定义 T(i) 为以i为根节点的子树种的所有点。

定义P(i)为 i 的所有祖先节点。

定义s(i)为从P(i)中的点到T(i)中的点存在的边的数量。

定义t(i)为T(i)中所有节点能够连接到的i的祖先节点中深度最大的深度。


如上图所示:其中T(3)={ 3 ,4,5 }

                                P(3)={ 2, 1 }

                                s(3)= 3

                                t (3)= 2

在dfs树种,回边只会连向自己的祖先,即不会存在横边的情况。

1.对任意一个节点来说,如果他的s = 1,那么可以断定它和它的父亲的连线段是桥。

2.若节点 i 的 s 值为 2 ,那么可以断定,只要将这两条边减除,就可以将 i 与 i 的祖先分开,因此答案+1。

3.若节点 i 和父亲的边不是桥,并且与父亲节点之间只有一条边,那么可以看出,i的后继中存在与i的祖先相连的边。

                                                     若在 i 的后继中找到某一个节点 j ,它与它的父亲同样只有一条边相连,

                                                                                                                   它的s值与 i 的 s 值相等

                                                                                                                   它的最深回边所到的点是 i 的祖先

                                                       那么,只要减除i与i父亲的边,以及j与j父亲的边,即可形成新的联通分支。如下图所示的橙色边:


将有桥的方案和无桥的方案相加,即可得出总方案数。

#include<iostream>#include<cstdio>#include<cstring>#include<vector>using namespace std;const int maxn=2005;struct Edge {    int a,b;};vector<Edge>edges;vector<int>G[maxn];int deep[maxn];int t[maxn];int s[maxn];short lowset[maxn][maxn];void add(int a,int b){    Edge e;e.a=a;e.b=b;    edges.push_back(e);    e.a=b;e.b=a;    edges.push_back(e);    G[a].push_back(edges.size()-2);    G[b].push_back(edges.size()-1);}int bridge;int nobridge;void init(int n){    bridge=nobridge=0;    memset(deep,-1,sizeof(deep));    memset(t,0,sizeof(t));    memset(s,0,sizeof(s));    for(int i=0;i<=n;i++)G[i].clear();    edges.clear();}int check(int u,int fa){    int cnt=0;    for(int i=0;i<G[fa].size();i++){        if(edges[G[fa][i]].b==u)cnt++;    }    return cnt;}bool Vis[maxn];void push_down(int u,int su,int du){    for(int i=0;i<G[u].size();i++){        int v=edges[G[u][i]].b;        if(deep[v]!=deep[u]+1)continue;        if(Vis[v])continue;        Vis[v]=true;        if(check(v,u)==1&&su==s[v]&&t[v]<du){            nobridge++;        }        push_down(v,su,du);    }}void dfs(int u,int fa,int No,int dep){    memset(lowset[u],0,sizeof(lowset[u]));    deep[u]=dep;    s[u]=1;    t[u]=1;    for(int i=0;i<G[u].size();i++){        if(G[u][i]==(No^1))continue;        int v=edges[G[u][i]].b;        if(deep[v]==-1){            dfs(v,u,G[u][i],dep+1);            for(int j=1;j<dep;j++){                if(lowset[v][j])lowset[u][j]+=lowset[v][j];            }        }        else if(deep[v]<deep[u]){            lowset[u][deep[v]]++;        }    }    for(int i=1;i<dep;i++){        if(lowset[u][i]){            s[u]+=lowset[u][i];            t[u]=t[u]>i?t[u]:i;        }    }    if(fa==0)s[u]=0;    if(s[u]==1)        bridge++;    else if(s[u]==2)   nobridge++;    if(s[u]!=0&&s[u]!=1&&check(u,fa)==1){        memset(Vis,0,sizeof(Vis));        push_down(u,s[u],deep[u]);    }}void read_int(int&x){    char ch=getchar();    while(ch<'0'||ch>'9') ch=getchar();    x = ch - '0';    ch=getchar();    while('0'<=ch&&ch<='9')    {        x = 10*x + ch-'0';        ch = getchar();    }}int main(){    int n,m,a,b;    while(scanf("%d%d",&n,&m)!=EOF){        init(n);        for(int i=0;i<m;i++){            read_int(a);            read_int(b);            add(a,b);        }        dfs(1,0,-1,1);        int res=bridge*(m-bridge)+(bridge*(bridge-1))/2;        printf("%d\n",res+nobridge);    }    return 0;}/*5 71 22 33 43 43 44 55 1
<span style="font-family: Arial, Helvetica, sans-serif;">ans = 6</span>
*/




0 0