Codeforces Round #311 (Div. 2)--D(图
题意:给出一个图,问最少加多少条边能连出一个奇圈,还要求输出连边的方法种数。
思路:比赛的时候看了一下是图相关的就没多想,也没时间了,其实挺简单的,如果已经有奇圈,那么直接输出0 1,如果最多有两个点相连,那么就是(n-2)×m,就是每条边和另外的任意点连两条边,要么就是没有边,这个就是直接输出结果。。
稍微麻烦一点的是最后一种,联通块有多个点且没有奇圈,这时候需要把联通快黑白染色,同色的点相连就是一个奇圈,dfs的时候统计每个联通块中两种=颜色的个数即可。
#include<bits/stdc++.h> #define pb push_back using namespace std; typedef long long ll; const int maxv=1e5+40; ll n,m; vector<int> G[maxv]; ll cont1[maxv],cont2[maxv]; bool vis[maxv]; bool col[maxv]; int contcomp=0; bool dfs(int u,bool c){ if(vis[u]){ if(col[u]==c){ return false; }else{ return true; } }else{ vis[u]=1; bool flag=1; col[u]=!c; ll *now=(!c?cont1:cont2); now[contcomp]++; for(int i=0;i<G[u].size();i++){ flag&=dfs(G[u][i],!c); } return flag; } } int main(){ ///freopen("/home/files/CppFiles/in","r",stdin); cin>>n>>m; for(int i=0;i<m;i++){ int f,t; scanf("%d%d",&f,&t); G[f].pb(t); G[t].pb(f); } if(m==0){ cout<<"3 "<<n*(n-1)*(n-2)/6<<endl; return 0; } bool flag=1; for(int i=1;i<=n;i++) if(G[i].size()>1) flag=0; if(flag){ cout<<"2 "<<(n-2)*m<<endl; return 0; } for(int i=1;i<=n;i++){ if(!vis[i]){ contcomp++; if(!dfs(i,0)){ cout<<"0 1"<<endl; return 0; } } } ll ans=0; for(int i=1;i<=contcomp;i++){ ans+=cont1[i]*(cont1[i]-1)/2+cont2[i]*(cont2[i]-1)/2; } cout<<"1 "<<ans<<endl; return 0; }