noip模擬賽(關於歐拉回路的一點想法)
阿新 • • 發佈:2018-12-15
題目大意:給出n個點m條邊,問有多少種方案可以走m-2條邊2次,走2條邊1次。邊為雙向邊。無重邊,有自環。
這道題用到了歐拉回路的一些思想(考試的時候我是通過對拍出所有情況討論過的!)
可以理解成花一個一筆畫,共用了2*m-2條邊。這種用不重複的邊走完全圖的操作就是歐拉回路。
而根據歐拉回路的性質,只有所有的點的度都是偶數或者只有兩個點的度是奇數就可以滿足。
而因為是雙向邊,所以所有點的度都是偶數, 那麼就只有以下幾種情況:
1.任意三個相連的點,連線他們的兩條邊去掉。這樣中間點的度還是偶數,左右點的度為奇數,滿足。
2.刪去一個自環和一條任意的邊,刪去自環仍然是偶數,一條邊刪去出現兩個奇數點。
3.刪去任意兩個自環,所有點還都是偶數。
所以只要在每個點的度中任取兩個都可以構成答案,對於環與邊的答案要單獨處理,對於環之間的答案一起處理。
任取兩個的方案數就是C(2,num[i](i點的度)),也就是num[i]*(num[i]-1)/2。
最後對於圖不連通的情況輸出0,不連通是指有邊的點之間不屬於一個集合。
#include<cstdio> #include<cmath> #include<algorithm> #include<cstring> using namespace std; typedef long long ll; ll ans; ll num[100005]; int f[100005]; int used[100005]; int tt; int cb[100005],ans1; int findf(int x) { if(x==f[x])return x; return f[x]=findf(f[x]); } int main() { int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)f[i]=i; for(int i=1;i<=m;i++) { int a,b; scanf("%d%d",&a,&b); num[a]++;num[b]++; if(a==b) { num[a]--; cb[++ans1]=a; } int f1=findf(a),f2=findf(b); if(f1!=f2)f[f1]=f2; used[a]++;used[b]++; } for(int i=1;i<=n;i++) { if(!used[i])continue; if(findf(i)==i)tt++; if(tt>1) { printf("%d",0); return 0; } } for(int i=1;i<=n;i++) { ans+=(ll)num[i]*(ll)(num[i]-1)/(ll)2; } for(int i=1;i<=ans1;i++) { ans+=(ll)m-(ll)num[cb[i]]-(ll)ans1+(ll)1; } ans+=(ll)ans1*(ll)(ans1-1)/(ll)2; printf("%lld",ans); }