問題へのソリューション - スタートレック(オイラーの道)
オイラーの問題は非常に巧妙な方法が、また、細部に注意を払います
フェイス質問
説明
タイトル顔が隠れて
入力
マップ
出力
最小のコストを表す整数
in.1
5 4
1 2
1 3
1 4
1 5
out.1
6
データ範囲と表記
データの100%に、N、M <= 1E5は、他の隠れ
考え
主なアイデアは、
質問の意味された、私たちはそれぞれの側を倍増して、すべての側面上の2つの異なる側面を選択し、それを削除する必要がある、またはそれらはオイラーオイラーの道を満たしている場合、あなたは本質的に異なる得ることができますプログラム。
これは、2つの部分に分け:
1.中国聯通の判断は、どれもあれば、プログラム番号は確かに0である
2分類統計処理貢献
治療:
ランタイムを構築するユニコムの分析1.図は、図の互いに素なセットを構築するために使用されなくてもよい、マーキング各エッジをDFS、両方の方法は、O(N)
2.さて、キーここに。
前提は:ダブルサイド後は、すべてのポイントの度合いが均一になります。
Cndition1: 2つのループバックを削除します。ポイントのリングからインパクトまでの偶数である、削除された任意の二つのループバックを選択するので、常にオイラーツアーを満たします。= C + ANS(2、M)
Cndition2: +ループバック側を削除します。点にリングからの衝撃に無視偶数であり、片側2点の次数が奇数となるので、常にオイラー道を満たします。リングの数からパスのANS + = X数
Cndition3:それは一つの共通点を持っていなければならない2つの辺を選択し、条件を満たしていることを確認するためにオイラー、オイラー道の条件を満たすことができない二つの側面を、削除します。そう初期度の各点について(ダブルない)> = 2、統計的寄与。ANS + =ΣC(2、デュ[I])。
詳細:
1.使用してDFSがユニコムを宣告した場合、リング、それ以外の場合はバーストスタックを終了した累積回数に注意を払います。
重い側が倍増した後に同じを削除することができないので、上記の実践後の図を削除2.ユニコムのままです。
DFSバージョン(ssw02から)
#include<bits/stdc++.h>
using namespace std ;
#define ll long long
const int MAXN = 100005 ;
inline int read(){
int s=0 ;char g=getchar(); while(g>'9'||g<'0')g=getchar() ;
while( g>='0'&&g<='9')s=s*10+g-'0',g=getchar() ; return s ;
}
ll ans = 0LL , sum = 0LL , cnt = 0LL , du[ MAXN ] ;
int N , M , head[ MAXN ] , to[ MAXN*2 ] , nex[ MAXN*2 ] , tot = 1 , vis[ MAXN ];//边 自环
bool used[ MAXN*2 ] , key ;
void add( int x , int y ){
to[ ++tot ] = y , nex[ tot ] = head[ x ] , head[ x ] = tot ;
}
void check(){
for( int i = 2 ; i <= tot ; ++i )
if( !used[ i ] ){
key = true ; break ;
}
}
void dfs( int u , int fa ){
vis[ u ]++ ;
if( vis[ u ] > 10 )return ;//平时开大点,这里数据水
for( int i = head[ u ] ; i ; i = nex[ i ] ){
used[ i ] = true ;
if( to[i] == fa || to[ i ] == u )continue ;
dfs( to[i] , u ) ;
}
}
int main(){
N = read() , M = read() ; int m1 , m2 ;
for( int i = 1 ; i <= M ; ++i ){
m1 = read() , m2 = read() ; add( m1 , m2 ) , add( m2 , m1 ) ;
if( m1 != m2 )
sum++ , du[ m1 ]++ , du[ m2 ]++ ;
else cnt++ ;
}
for( int i = 1 ; i <= N ; ++i )
if( head[ i ] ){dfs( i , i ) ; break ;}
check();
if( key ){ printf("0");return 0 ;}
if( cnt >= 2 )
ans = (ll)cnt*(ll)(cnt-1LL)/2LL ;
if( cnt >= 1 && sum >= 1 )
ans += (ll)sum*cnt ;
if( sum >= 2 )
for( int i = 1 ; i <= N ; ++i )
ans += ( (ll)du[i]*(du[i]-1LL) )/2LL ;
cout<<ans ;
}
互いに素セット版(STD)
#include<cstdio>
#include<iostream>
using namespace std;
#define maxn 1000010
int fa[maxn],siz[maxn];
int findfa(int p)
{
if(fa[p]!=p) fa[p]=findfa(fa[p]);
return fa[p];
}
void uni(int p,int q)
{
p=findfa(p),q=findfa(q);
if(p!=q)
{
fa[p]=q;
siz[q]+=siz[p];
}
}
int n,m,l[maxn],r[maxn];
int du[maxn],sig;
bool v[maxn];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++) fa[i]=i,siz[i]=1;
for(int i=1; i<=m; i++)
{
scanf("%d%d",&l[i],&r[i]);
uni(l[i],r[i]);
if(l[i]!=r[i])
{
du[l[i]]++;
du[r[i]]++;
}
else sig++;
}
int lst=findfa(l[1]);
for(int i=2; i<=m; i++)
if(findfa(l[i])!=lst) {lst=-1; break;}
if(lst==-1) cout<<0<<endl;
else
{
long long ans=0;
ans+=1ll*sig*(sig-1)/2;
ans+=1ll*sig*(m-sig);
for(int i=1; i<=n; i++)
ans+=1ll*du[i]*(du[i]-1)/2;
cout<<ans<<endl;
}
return 0;
}