$Billions\ of\ lighthouses...stuck\ at\ the\ far\ end\ of\ the\ sky.$c++
平面有$n$個燈塔,初始時兩兩之間能夠相互交流;但因爲地形緣由,有$m$對燈塔之間沒法進行直接的交流。也就是一張徹底圖缺乏了$m$條邊。
$River$想把這$n$個燈塔連成一個環,使得$n$個等他都在環上,而且環上相鄰的兩個燈塔能進行直接交流。$River$想知道這樣作的方案數是多少,兩種方案被認爲是不一樣的,當且僅當有兩個燈塔$u,v$,他們在一種方案中在環上相鄰,而在另外一種方案中相反。
答案可能很大,你只須要輸出對${10}^9+7$取模的結果。spa
第一行兩個整數$n,m$。
接下來$m$行,每行描述一條缺乏的邊。blog
一行一個整數表示答案。it
樣例輸入1:io
4 1
1 2class
樣例輸出1:map
1im
樣例輸入2:數據
10 3
1 9
3 8
2 7img
樣例輸出2:
87840
樣例$1$解釋:
惟一的方案是(1,3,2,4)依次連成環。
數據範圍:
對於全部數據,有$3\leqslant n\leqslant {10}^7,0\leqslant m\leqslant \min(20,\frac{n(n-1)}{2})$。輸入的邊中沒有重邊。
題目實際上球的就是哈密頓迴路的數量。因爲$m$很小,考慮容斥。
枚舉刪除的邊的某個子集$S$,設$f_S$表示有多少條哈密頓迴路至少包含$S$集合中的邊,答案就是$\sum_S(-1)^{|S|}\times f_S$。
怎麼算$f_S$呢?首先判掉$f_S=0$的狀況,這包含如下兩種狀況:
$\alpha.$僅考慮$S$中的邊時,某個點的度數大於$2$。
$\beta.$出現了環,而且這個環的大小不爲$n$。
特判掉$S$自己就是一個哈密頓迴路的狀況;假設$S$中的邊構成了$k$條鏈,那麼$f_S=s{k-1}\times (n-|S|-1)!$。
證實:考慮將一條鏈當作一個點,那麼總共有$n-|S|$個點,其環排列方案數爲$(n-|S|-1)!$;每條鏈均可以翻轉,所以乘上$2^k$;又因爲一條哈密頓迴路對應了兩個環排列(正反兩個方向),還要除以$2$。
時間複雜度:$\Theta(2^m\times m)$。
指望得分:$100$分。
實際得分:$100$分。
#include<bits/stdc++.h>
using namespace std;
int n,m;
long long jc[10000001];
int fa[10000001];
pair<int,int> pos[10000001];
int tot;
long long ans;
int cnt[10000001],vis[10000001],g[10000001];
int que[10000001];
map<pair<int,int>,bool> h;
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
long long qpow(long long x,long long y)
{long long res=1;while(y){if(y&1)res=res*x%1000000007;x=x*x%1000000007;y>>=1;}return res;}
long long solve(int x)
{
int sum=0,k=que[0]=0;
bool flag=0;
for(int i=1;i<=tot;i++)
if((x>>(i-1))&1)
{
que[++que[0]]=i;
fa[pos[i].first]=pos[i].first;
fa[pos[i].second]=pos[i].second;
vis[pos[i].first]=vis[pos[i].second]=g[pos[i].first]=g[pos[i].second]=0;
}
for(int i=1;i<=que[0];i++)
{
if(!vis[pos[que[i]].first])sum++;
if(!vis[pos[que[i]].second])sum++;
if(vis[pos[que[i]].first]==2||vis[pos[que[i]].second]==2)return 0LL;
vis[pos[que[i]].first]++;
vis[pos[que[i]].second]++;
}
for(int i=1,u,v;i<=que[0];i++)
{
if((u=find(pos[que[i]].first))==(v=find(pos[que[i]].second)))flag=1;
fa[u]=v;
}
for(int i=1,u;i<=que[0];i++)
if(!g[u=find(pos[que[i]].first)]){g[u]=1;k++;}
if(flag&&(cnt[x]!=n||k>1))return 0LL;
long long res=jc[n-sum+k-1]*qpow(2,k)%1000000007*qpow(2,1000000005)%1000000007;
return (cnt[x]&1)?-res:res;
}
int main()
{
jc[0]=1;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
if(x==y||h[make_pair(x,y)])continue;
h[make_pair(x,y)]=1;
pos[++tot]=make_pair(x,y);
}
for(int i=1;i<=n;i++)jc[i]=1LL*i*jc[i-1]%1000000007;
for(int i=0;i<(1<<tot);i++)cnt[i]=cnt[i>>1]+(i&1);
for(int i=0;i<(1<<tot);i++)ans=(ans+solve(i))%1000000007;
printf("%lld",(ans+1000000007)%1000000007);
return 0;
}
rp++