其實挺難的,對prufer得有必定的理解。html
首先咱們知道prufer序列最多有n-2個點,其他知道度數的點出現的次數是di-1,定義prufer序列中剩餘出現次數sum=n-2-∑(di-1)。這樣咱們能夠近似於咱們已經知道全部點的出現次數,用prufer的公式$\frac{(n-2)!}{\prod_{i=1}^n(d_i-1)!sum!}$,其中di是已知的度數,不必定由1到n,那個公式是搬過來的。c++
這樣的話咱們忽略了一個問題,咱們只是近似地認爲全部點出現的次數已知,其實sum那一部分還有不少狀況被忽略,由於咱們並不知道剩下的位置究竟是誰,那麼,若是還剩下m個點不知道度數,那麼在sum個位置任意填上他們的方案數爲$m^{sum}$。把這兩部分乘到一塊兒就是答案。ide
還有一個問題,這個東西是要高精的,這一坨坨的階乘很是適合直接用階乘拆分(至於若是你不知道什麼是階乘拆分,請看這個小連接),而後用qpow乘到一塊兒,只是有點懶,直接for往上乘的也能夠,沒有qpow,也沒有億進制優化。優化
#include<bits/stdc++.h> #define int long long using namespace std; int n,du[2000]; int prime[1000],prime_num,sum,m; bool v[2000]; struct Bigint{ int a[100000],len; void clear(){ memset(a,0,sizeof(a)); a[1]=1; len=1; } friend void operator *(Bigint &x,int y){ int delta=0; for(int i=1;i<=x.len;i++){ x.a[i]=x.a[i]*y+delta; delta=x.a[i]/10; x.a[i]%=10; } while(delta){ x.a[++x.len]=delta%10; delta/=10; } while(x.a[x.len]==0&&x.len>1) x.len--; } void out(){ for(int i=len;i>=1;i--) printf("%lld",a[i]); } }ans; int read(){ int sum=0,f=1;char x=getchar(); while(x<'0'||x>'9'){ if(x=='-') f=-1; x=getchar(); }while(x>='0'&&x<='9'){ sum=sum*10+x-'0'; x=getchar(); }return sum*f; } void doprime(int x){ for(int i=2;i<=x;i++){ if(!v[i]) prime[++prime_num]=i; for(int j=1;j<=prime_num&&i*prime[j]<=x;j++){ v[i*prime[j]]=1; if(i%prime[j]==0) break; } } } signed main(){ //freopen("data.in","r",stdin); //freopen("2.out","w",stdout); n=read(); for(int i=1,x;i<=n;i++){ x=read(); /* if(!x){ puts("0"); return 0; }*/ if(x==-1) continue; du[++du[0]]=x; sum+=du[du[0]]-1; m++; if(n-2<sum){ puts("0"); return 0; } } sum=n-2-sum; m=n-m; // cout<<m<<" "<<sum<<endl; doprime(n);ans.clear(); /* for(int i=1;i<=prime_num;i++) cout<<prime[i]<<" ";cout<<endl;*/ for(int i=1;i<=prime_num;i++){ int s=0; for(int j=n-2;j/=prime[i];) s+=j; //cout<<"s1="<<s<<endl; for(int j=1;j<=du[0];j++) for(int k=du[j]-1;k/=prime[i];) s-=k; for(int j=sum;j/=prime[i];) s-=j; for(int j=1;j<=s;j++) ans*prime[i]; } for(int i=1;i<=sum;i++) ans*m; ans.out(); puts(""); return 0; }