hdu 5215(边-双连通分量)


Cycle

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)


Problem Description
Ery is interested in graph theory, today he ask BrotherK a problem about it: Given you a undirected graph with N vertexes and M edges, you can select a vertex as your starting point, then you need to walk in the graph along edges. However, you can't pass a edge more than once, even opposite direction is forbidden. At the end, you should come back to the starting point. Assume you has passed X edges, there are two questions:

Question 1: Can X be a odd number ?

Question 2: Can X be a even number ?

(note: you must walk, so X can't be 0)
 

Input
The first line contains a single integer T , indicating the number of test cases.

Each test case begins with two integer N, M , indicating the number of vertexes and the number of edges. Following M lines, each line contains two integers Ui, Vi , indicating there are a edge between vertex Ui and vertex Vi .

T is about 30

1  N  100000

0  M  300000

1  Ui,Vi  N

Ui will not equal to Vi

There is at most one edge between any pair of vertex.
 

Output
For each test, print two lines.

The first line contains "YES" or "NO" for question 1.

The second line contains "YES" or "NO" for question 2.
 

Sample Input
  
  
3 1 0 3 3 1 2 2 3 3 1 4 4 1 2 2 3 3 4 4 1
 

Sample Output
  
  
NO NO YES NO NO YES
Hint
If you need a larger stack size, please use #pragma comment(linker, "/STACK:102400000,102400000") and submit your solution using C++.


首先我们注意到一个环一定存在于双联通分量(既去掉任何一条边后仍然联通的点集)内

通过tarjan算法,可以分离出所有的双联通分量,然后分别检查其中是否存在偶环
对于一个双联通分量,如果它仅仅是一个环,那么只需判断它是否是偶环即可
否则其中必定存在两个缠绕(共享至少一个点)的环,若这两个环的都是奇环,那么去掉共享的边之后可以组成一个大偶环
因此,除非一个双联通分量仅仅是一个奇环,那么其中必定存在偶环
而我们可以发现,若一个非单点的双联通分量仅仅由一个环构成,那么它的点数必定等于边数,据此判断即可
时间复杂度O(N + M)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;

const int MAXN = 100010;
const int MAXM = 300010;
struct Edge
{
    int to, next;
    bool cut;
} edge[MAXM << 1];
int head[MAXN], tot;
int Low[MAXN], DFN[MAXN], Stack[MAXN];
int Index, top;
bool Instack[MAXN];

void addedge(int u, int v)
{
    edge[tot].to = v;
    edge[tot].next = head[u];
    edge[tot].cut = false;
    head[u] = tot++;
}

void Tarjan(int u, int pre)
{
    int v;
    Low[u] = DFN[u] = ++Index;
    Stack[top++] = u;
    Instack[u] = true;
    for (int i = head[u]; i != -1; i = edge[i].next)
    {
        v = edge[i].to;
        if (v == pre)continue;
        if (!DFN[v])
        {
            Tarjan(v, u);
            if (Low[u] > Low[v])
                Low[u] = Low[v];
            if (Low[v] > DFN[u])
            {
                edge[i].cut = true;
                edge[i ^ 1].cut = true;
            }
        }
        else if (Instack[v] && Low[u] > DFN[v])
            Low[u] = DFN[v];
    }
    if (Low[u] == DFN[u])
    {
        do
        {
            v = Stack[--top];
            Instack[v] = false;
        } while (v != u);
    }
}

void init()
{
    tot = 0;
    memset(head, -1, sizeof(head));
}


void solve(int n)
{
    memset(DFN, 0, sizeof(DFN));
    memset(Instack, false, sizeof(Instack));
    Index = top = 0;
    for (int i = 1; i <= n; i++)
        if (!DFN[i])
            Tarjan(i, i);
}

int num;
int vis[MAXN], dep[MAXN], cnt[MAXN];
bool odd, even;

void dfs(int u, int depth, int pre)
{
    vis[u] = 1; //vis标记的是双连通分量的点
    dep[u] = depth; 
    cnt[u] = 1; //cnt标记的是当前遍历到的点
    for (int i = head[u]; i != -1; i = edge[i].next)
    {
        if (edge[i].cut) continue;
        int v = edge[i].to;
        if(v == pre) continue;
        if (vis[v])
        {
            if (cnt[v])
            {
                int len = dep[u] - dep[v] + 1;
                if (len % 2 == 1)
                {
                    odd = true;
                    num++;   //奇环的个数
                    if (num > 1)
                        even = true;
                }
                else
                    even = true;
            }
        }
        else
            dfs(v, depth + 1, u);
    }
    cnt[u] = 0;
}

int main()
{
    int t;
    scanf("%d", &t);
    while (t--)
    {
        init();
        int n, m;
        scanf("%d%d", &n, &m);
        for (int i = 0; i < m; i++)
        {
            int u, v;
            scanf("%d%d", &u, &v);
            addedge(u, v);
            addedge(v, u);
        }
        solve(n);

        odd = even = false;
        memset(vis, 0, sizeof(vis));
        memset(cnt, 0, sizeof(cnt));
        for (int i = 1; i <= n; i++)
        {
            if (!vis[i])
            {
                num = 0;
                dfs(i, 1, -1);
            }
        }

        printf("%s\n", odd ? "YES" : "NO");
        printf("%s\n", even ? "YES" : "NO");
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值