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;
}
View Code

 

posted @ 2015-07-02 00:09  PlusSeven  阅读(131)  评论(0编辑  收藏  举报