其实挺难的,对prufer得有一定的理解。
首先我们知道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,那个公式是搬过来的。
这样的话我们忽略了一个问题,我们只是近似地认为所有点出现的次数已知,其实sum那一部分还有很多情况被忽略,因为我们并不知道剩下的位置到底是谁,那么,如果还剩下m个点不知道度数,那么在sum个位置任意填上他们的方案数为$m^{sum}$。把这两部分乘到一起就是答案。
还有一个问题,这个东西是要高精的,这一坨坨的阶乘非常适合直接用阶乘拆分(至于如果你不知道什么是阶乘拆分,请看这个小链接),然后用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; }