题目链接:http://codeforces.com/problemset/problem/557/D
大意 给出一个未必联通的无向图(点数至少为3),问最少加多少边可以形成一个奇圈,以及这样做的方案有多少种。
首先如果是一张没有边的图,那么直接就是需要加三条边,有C(n,3)种方式。
接着,判断这张图每一个联通块是否是一个二分图,因为二分图是一定没有奇圈的。如果有联通块不是二分图,那么也就是意味着存在奇圈,这样的话需要加0条边,方式为1种。
接下去还需要分两种情况讨论
1.如果所有的联通块都只有1个或者2个点,则至少需要加2条边,方式为所有点数为2的联通块随便选择一个其他的点加2条边,故统计所有点数为2的联通块数量。
2.存在至少包含3个点的联通块,注意,此时已经排除了联通块不是二分图的情况,所以也即联通块一定是二分图。对于二分图,连接x部和y部的边是不会形成奇圈的,这种情况下只需要连接一条相同部之间的边即可。所以方案数为对于所有至少包含3个点的联通块,计算x部和y部点数,对答案累加上 C(cntx,2)+C(cnty,2)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 #include <string> 5 #include <string.h> 6 #include <stdio.h> 7 #include <math.h> 8 #include <stdlib.h> 9 #include <queue> 10 #include <stack> 11 #include <map> 12 #include <set> 13 14 using namespace std; 15 16 const int N=1e5+100; 17 18 vector<int> edge[N]; 19 bool vis[N]; 20 int col[N]; 21 bool found=false; 22 int cnt[2]; 23 void dfs(int u,int c) { 24 vis[u]=true; 25 col[u]=c; 26 cnt[c]++; 27 for (int i=0;i<edge[u].size();i++) { 28 int v=edge[u][i]; 29 if (vis[v]&&col[v]==c) { 30 found=true; 31 } 32 if (vis[v]) continue; 33 dfs(v,c^1); 34 } 35 } 36 int main(){ 37 int n,m; 38 scanf("%d %d",&n,&m); 39 for (int i=1;i<=m;i++) { 40 int u,v; 41 scanf("%d %d",&u,&v); 42 edge[u].push_back(v); 43 edge[v].push_back(u); 44 } 45 if (m==0) { 46 long long ret=1LL*n*(n-1)*(n-2)/6LL; 47 cout<<3<<" "<<ret<<endl; 48 } 49 else { 50 found=false; 51 bool three=false; 52 long long retThree=0; 53 int cnt2=0; 54 for (int i=1;i<=n&&!found;i++) { 55 if (vis[i]) continue; 56 cnt[0]=cnt[1]=0; 57 dfs(i,0); 58 if (cnt[0]+cnt[1]>=3){ 59 three=true; 60 retThree+=1LL*cnt[0]*(cnt[0]-1)/2LL+1LL*cnt[1]*(cnt[1]-1)/2LL; 61 } 62 else if (cnt[0]+cnt[1]==2) 63 cnt2++; 64 } 65 if (found) { 66 cout<<0<<" "<<1<<endl; 67 } 68 else if (three){ 69 cout<<1<<" "<<retThree<<endl; 70 } 71 else { 72 long long ret=1LL*cnt2*(n-2); 73 cout<<2<<" "<<ret<<endl; 74 } 75 } 76 return 0; 77 }