题目链接: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;
}