51nod 1806 wangyurzee的樹
阿新 • • 發佈:2017-05-26
memset fine sin ace efi res pri () 空間限制
基準時間限制:1 秒 空間限制:131072 KB
wangyurzee有n個各不相同的節點,編號從1到n。wangyurzee想在它們之間連n-1條邊,從而使它們成為一棵樹。
可是wangyurzee發現方案數太多了,於是他又給出了m個限制條件,其中第i個限制條件限制了編號為u[i]的節點的度數不能為d[i]。
一個節點的度數,就是指和該節點相關聯的邊的條數。
這樣一來,方案數就減少了,問題也就變得容易了,現在請你告訴wangyurzee連邊的方案總數為多少。
答案請對1000000007取模。
樣例解釋
總方案共有3種,分別為{(1,2),(1,3)},{(1,2),(2,3)},{(2,3),(1,3)}。其中第二種方案節點1的度數為2,不符合要求,因此答案為2。 Input
Output
現在有x個點的貢獻確定了,其度數總和為
可是wangyurzee發現方案數太多了,於是他又給出了m個限制條件,其中第i個限制條件限制了編號為u[i]的節點的度數不能為d[i]。
一個節點的度數,就是指和該節點相關聯的邊的條數。
這樣一來,方案數就減少了,問題也就變得容易了,現在請你告訴wangyurzee連邊的方案總數為多少。
答案請對1000000007取模。
樣例解釋
總方案共有3種,分別為{(1,2),(1,3)},{(1,2),(2,3)},{(2,3),(1,3)}。其中第二種方案節點1的度數為2,不符合要求,因此答案為2。 Input
第一行輸入2個整數n(1<=n<=1000000),m(0<=m<=17)分別表示節點個數以及限制個數。 第2行到第m+1行描述m個限制條件,第i+1行為2個整數u[i],d[i],表示編號為u[i]的節點度數不能為d[i]。 為了方便起見,保證1<=ui<=m。同時保證1<=ui<=n,1<=di<=n-1,保證不會有兩條完全相同的限制。
輸出一行一個整數表示答案。Input示例
3 1 1 2Output示例
2
樹 prufer編碼 數學問題 容斥
算度數不為d[i]的方案數看上去不可做,考慮算度數為d[i]的方案數。
首先我們知道n個點有標號生成樹的數量為 $ n^{n-2} $
註意到限制條件m很小,可以計算不滿足一個條件的方案數,不滿足兩個條件的方案數,不滿足三個條件的方案數……然後容斥一下。
假設當前計算不滿足某x個條件的方案數:
若一個點的度數為 $ d[i] $,那麽它在prufer序列中出現了 $ d[i]-1 $次。
現在有x個點的貢獻確定了,其度數總和為
$ \sum_{i=1}^{x} d[i] $
那麽在prufer序列中有
$ sum=\sum_{i=1}^{x} (d[i]-1) $ 個位置被占用。
占用這麽多位置的方案數是
$ C(n-2, sum)$
這些位置裏選$d[1]-1$個位置填第1種編號,方案數為
$ C(sum,d[1]-1)$
再選位置填第2種編號,方案數為
$ C(sum-d[1]-1,d[2]-1)$
以此類推
根據乘法原理把上面這些組合數乘起來,化簡得到:
$ \frac{(n-2)!}{(n-2-sum)! * \Pi (d[i]-1)!}$
prufer序列中剩下的位置可以任意填不被限制度數的點,共有
$(n-x)^{n-2-sum}$ 種方案
所以符合當前度數限制的方案數有
$ \frac{(n-2)!}{(n-2-sum)! * \Pi (d[i]-1)!} * (n-x)^{n-2-sum}$
註意:可能出現兩個限制條件同時限制一個點的度數,需要特判
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<cstdio> 5 #include<cmath> 6 #define LL long long 7 using namespace std; 8 const int mod=1e9+7; 9 const int mxn=1000050; 10 int read(){ 11 int x=0,f=1;char ch=getchar(); 12 while(ch<‘0‘ || ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} 13 while(ch>=‘0‘ && ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();} 14 return x*f; 15 } 16 int inv[mxn],fac[mxn]; 17 void init(int n){ 18 n+=5; 19 inv[0]=inv[1]=1;fac[0]=fac[1]=1; 20 for(int i=2;i<=n;i++){ 21 fac[i]=(LL)fac[i-1]*i%mod; 22 inv[i]=((-mod/i*(LL)inv[mod%i])%mod+mod)%mod; 23 } 24 for(int i=2;i<=n;i++)inv[i]=(LL)inv[i-1]*inv[i]%mod; 25 return; 26 } 27 int n,m; 28 int u[20],d[20];bool vis[20]; 29 LL ans=0; 30 int ksm(int a,int k){ 31 int res=1; 32 while(k){ 33 if(k&1)res=(LL)res*a%mod; 34 a=(LL)a*a%mod; 35 k>>=1; 36 } 37 return res; 38 } 39 int main(){ 40 int i,j; 41 n=read();m=read(); 42 if(n==1){printf("1\n");return 0;} 43 init(n); 44 for(i=0;i<m;i++){ 45 u[i]=read();d[i]=read(); 46 } 47 ans=ksm(n,n-2); 48 int ed=1<<m; 49 for(int S=1;S<ed;S++){//枚舉狀態 50 int tmp=S,smm=0,cnt=0;bool flag=1; 51 LL down=1; 52 memset(vis,0,sizeof vis); 53 for(i=0;i<m;i++){ 54 if((S>>i)&1){ 55 if(vis[u[i]]){flag=0;break;}//限制重復 56 vis[u[i]]=1; 57 smm+=d[i]-1; 58 ++cnt; 59 down=down*inv[d[i]-1]%mod; 60 } 61 } 62 if(!flag)continue; 63 if(smm>n-2)continue; 64 LL up=fac[n-2]; 65 up=up*down%mod*inv[n-2-smm]%mod; 66 up=up*ksm(n-cnt,n-2-smm)%mod; 67 (ans+=(cnt&1)?-up:up)%=mod; 68 } 69 ans=(ans+mod)%mod; 70 printf("%lld\n",ans); 71 return 0; 72 }
51nod 1806 wangyurzee的樹