题意:给出一个 n 点 m 边的图,问最少加多少边使其能够存在奇环,加最少边的情况数有多少种

奇环和偶环其实就是二分图的性质:二分图不存在奇环,所以只要判断这张图是否是二分图就行了:

如果本身就不是二分图,那么说明图中必定有奇环,那就不需要加边,情况数也就是1种了;

而如果是普通的二分图的话,只要有某一个区块有一半点数大于等于2,那么只要将同一边的任意两点连线,就可以使其不是二分图,从而出现奇环,加边数1条,情况数就是算有多少半点数大于等于2的,从中取两点的情况数;

如果全部都是两点图的话,就要对某个两点图,将两点同时连向其他任意一点,就需要加2条边,情况数就是两点组数乘剩余点数;

最后是全部为单点的图,必须连3条线,将任意三点两两相连,情况数就是任取三点的组合数。

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<algorithm>
 4 using namespace std;
 5 typedef long long ll;
 6 
 7 int c[100005];
 8 int n,m,num[2];
 9 int head[100005],nxt[200005],point[200005],size=0;
10 bool f=0;
11 
12 void add(int a,int b){
13     point[size]=b;
14     nxt[size]=head[a];
15     head[a]=size++;
16     point[size]=a;
17     nxt[size]=head[b];
18     head[b]=size++;
19 }
20 
21 void dfs(int a,int x){
22     if(f)return;
23     c[a]=x;
24     num[x]++;
25     for(int i=head[a];~i;i=nxt[i]){
26         int b=point[i];
27         if(c[b]==-1)dfs(b,!x);
28         else if(c[b]==x){
29             f=1;
30             return;
31         }
32     }
33 }
34 
35 int main(){
36     scanf("%d%d",&n,&m);
37     if(m==0){
38         ll ans=(ll)n*(ll)(n-1)*(ll)(n-2)/6;
39         printf("3 %I64d\n",ans);
40         return 0;
41     }
42     int i;
43     memset(head,-1,sizeof(head));
44     memset(c,-1,sizeof(c));
45     for(i=1;i<=m;i++){
46         int a,b;
47         scanf("%d%d",&a,&b);
48         add(a,b);
49     }
50     ll ans=0,ans2=0;
51     for(i=1;i<=n&&(!f);i++){
52         if(c[i]==-1){
53             num[0]=num[1]=0;
54             dfs(i,1);
55             ans+=(ll)num[0]*(ll)(num[0]-1)/2;
56             ans+=(ll)num[1]*(ll)(num[1]-1)/2;
57             if(num[0]==1&&num[1]==1){
58                 ans2+=n-2;
59             }
60         }
61     }
62     if(f)printf("0 1\n");
63     else if(ans==0)printf("2 %I64d\n",ans2);
64     else printf("1 %I64d\n",ans);
65     return 0;
66 }
View Code