其实挺难的,对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; }