Codeforces Round #311 (Div. 2) D. Vitaly and Cycle
解法
辺が1つもない場合は3本辺を追加するしかない。この場合は頂点の選び方でnC3通り。
次に,グラフが二部グラフでない場合は,奇数長の閉路が存在することになるのでこの場合は0本辺追加となる。
グラフが二部グラフの場合は,任意の閉路は偶数長になる(白->黒->白->黒みたいに色を交互に移るので元の頂点に戻るためには少なくとも色は同じでなければならず,色を同じにするためには閉路は偶数でなければならない)ので,上のことが言える。ちなみに,任意の閉路が偶数長->そのグラフは二部グラフということも言えるようです。
また,任意の点の次数が1以下の場合は,追加の辺は2本必要。この場合は,辺1本と頂点1つでひとつの奇数長閉路ができるので数は(n-2)*mとなる。
最後に残った辺が1本だけ必要な場合は,各連結成分において白と白をつなげる,のような感じと黒と黒をつなげるというようなものを数えていけば良い。
const int MAXN = 100010; int n, m; int a[MAXN], b[MAXN]; vector<int> G[MAXN]; int color[MAXN]; bool visit[MAXN]; ll white = 0, black = 0; bool dfs(int cur, int c) { color[cur] = c; for (int next : G[cur]) { if (color[next] == c) return false; if (color[next] == -c) continue; if (!dfs(next, -c)) return false; } return true; } void DFS(int cur) { visit[cur] = true; if (color[cur] == 1) white++; else black++; for (int next : G[cur]) { if (visit[next]) continue; DFS(next); } } int main() { cin.tie(0); ios::sync_with_stdio(false); cin >> n >> m; for (int i = 0; i < m; i++) { cin >> a[i] >> b[i]; G[a[i]].push_back(b[i]); G[b[i]].push_back(a[i]); } if (m == 0) { ll ans = 1; for (int i = 0; i < 3; i++) ans *= n-i; ans /= 6; cout << 3 << " " << ans << endl; return 0; } // 二部グラフになるかを調べる // ならなければ奇数長の閉路が存在 for (int i = 1; i <= n; i++) { if (color[i] == 0) { if (!dfs(i, 1)) { cout << 0 << " " << 1 << endl; return 0; } } } bool ng = true; for (int i = 1; i <= n; i++) { if (G[i].size() >= 2) { ng = false; break; } } if (ng) { cout << 2 << " " << (ll)(n-2) * m << endl; return 0; } ll ans = 0; for (int i = 1; i <= n; i++) { if (!visit[i]) { white = 0, black = 0; DFS(i); ans += white*(white-1) / 2; ans += black*(black-1) / 2; } } cout << 1 << " " << ans << endl; return 0; }