codeforces557D 二分图+奇环判断

题目链接:http://codeforces.com/problemset/problem/557/D


题目大意:给你一个无向图,n个节点m条边,问最少添加多少条边使图存在奇环(环的节点个数为奇数,环中没有重复节点),并且有多少种方案数


思路:1、若无边,即m等于0,则人选3个节点即可,答案为C(n ,3) = n * (n-1)*(n-2) / 6;   答案为 3  C(n , 3)

2、若任意边都没有交点,即所有点的度都小于等于1,则每条边的两个点在任选剩余节点中的一个点即可,这种情况需要增加两条边,每条边的方案数为(n-2),总共有m条边,答案就是 2  m*(n-2);


3、 本身存在奇环,答案是 0  1    (判断奇环方法,染色判断,相邻节点同色则存在奇环)


4、若不存在奇环,则看每个 联通块中黑白节点的数量,因为只有同色节点相连才会出现奇环,所以每个联通块贡献的方案数是:white[i]*(white[i]-1)/2 + black[i]*(blacki] - 1)/2  ,这个公式就是简单的数学。然后累加每个联通块的和即可。联通块 的个数在第三步进行染色的时候就可以判断,记录所有节点的父节点,顺便记录父节点内黑白节点数量。


ps:一定要开long long ,int相乘会爆,所以还要强制转换才可以。。。。。。


#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <iomanip>

using namespace std;
//#pragma comment(linker, "/STACK:102400000,102400000")
#define maxn 1000050
#define MOD 1000000007
#define mem(a , b) memset(a , b , sizeof(a))
#define LL long long
#define ULL unsigned long long
typedef pair<int , int> pii;
const long long INF= 0x3fffffff;
int n , m , id , flag;
struct node
{
    int u , v;
    int next;
}e[maxn];
int head[maxn] , d[maxn] , fa[maxn];
int color[maxn] , black[maxn] , white[maxn];

void add(int u , int v)
{
    e[id].u = u;
    e[id].v = v;
    e[id].next = head[u];
    head[u] = id ++;
}

void init()
{
    mem(head , -1);
    mem(d , 0);
    mem(black , 0);
    mem(white , 0);
    mem(fa , 0);
    mem(color , 0);
    id = 0;
}

void dfs(int u , int c , int f)
{
    color[u] = c;
    fa[u] = f;
    if(c == 1) ++black[f];
    else ++white[f];
    for(int i = head[u] ; i != -1 ; i = e[i].next)
    {
        if(!color[e[i].v] ) dfs(e[i].v , 3 - c , f);
        else if(color[e[i].v] == color[u])    //相邻两点奇偶性相同,存在奇环
        {
            flag = 1;
            //cout << u << " " << e[i].v << endl;
            return ;
        }
    }
}

int main()
{
    while(scanf("%d %d" , &n , &m) != EOF)
    {
        int u , v , cnt = 0;
        init();
        for(int i = 0 ; i < m ; i ++)
        {
            scanf("%d %d" , &u , &v);
            add(u , v);
            add(v , u);
            d[u]++;
            d[v]++;
        }
        if(m == 0)                  //增加3条边
        {
            LL tmp = (LL)n * (n -1) * (n - 2) / 6;
            cout << 3 << " " << tmp << endl;
            continue;
        }
        flag = 0;
        for(int i = 1 ; i <= n ;i  ++)
        {
            if(d[i] >= 2) {flag = 1;break;}
        }
        if(!flag)           //增加两条边   , 所有点度数为1,即没有相连的时候。每条边都有n-2中方案数
        {
            LL tmp = (LL)m * (n - 2);
            cout << 2 << " " << tmp << endl;
            continue;
        }
        flag = 0;
        for(int i = 1 ; i <= n ; i ++)
        {
            if(!color[i])
            {
                dfs(i , 1 , ++ cnt);
                if(flag)
                {
                    cout << 0 << " " << 1 << endl;
                    break;
                }
            }
        }
        if(flag) continue;
        LL ans = 0;
        for(int i = 1 ; i <= cnt ; i ++)
        {
            ans += (LL)white[i] * (white[i] - 1) / 2 + (LL)black[i] * (black[i] - 1) / 2;
        }
        cout << 1 << " " << ans << endl;
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值