BZOJ 1005 明明的烦恼 Prufer序列+组合数学+高精度
题目大意:给定一棵n个节点的树的节点的度数。当中一些度数无限制,求能够生成多少种树
Prufer序列
把一棵树进行下面操作:
1.找到编号最小的叶节点。删除这个节点,然后与这个叶节点相连的点计入序列
2.重复进行1,直到这棵树仅仅剩下两个节点时,退出
比方说这个图(来自度受百科)
最小叶节点为2,删除2,将3计入序列
最小叶节点为4,删除4,将5计入序列
最小叶节点为5,删除5,将1计入序列
最小叶节点为1,删除1。将3计入序列
图中仅仅剩下两个节点,退出
于是得到这棵树的Prufer序列为{3,5,1,3}
这样能够得到一个长度为n-2的序列。非常easy证明,树和Prufer序列是一一相应的
Prufer序列显然满足一个性质:一个点若度数为d,则一定在Prufer序列中出现了d-1次
于是这就变成了一个排列组合的问题了
令每一个已知度数的节点的度数为di,有n个节点,m个节点未知度数。left=(n-2)-(d1-1)-(d2-1)-...-(dk-1)
已知度数的节点可能的组合方式例如以下
(n-2)!/(d1-1)!/(d2-1)!/.../(dk-1)!/left!
剩余left个位置由未知度数的节点任意填补,方案数为m^left
于是最后有
ans=(n-2)!/(d1-1)!/(d2-1)!/.../(dk-1)!/left! * m^left
答案非常显然有高精度。为了避免高精度除法我们能够对每一个阶乘暴力分解质因数,对指数进行加减操作就可以
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define M 1100 using namespace std; typedef long long ll; struct abcd{ ll xx[400]; int cnt; abcd(int x=0) { memset(xx,0,sizeof xx); xx[1]=x; cnt=1; } ll& operator [] (int x) { return xx[x]; } }ans(1); abcd operator *= (abcd &x,abcd &y) { int i,j; abcd z; for(i=1;i<=x.cnt;i++) for(j=1;j<=y.cnt;j++) z[i+j-1]+=x[i]*y[j],z[i+j]+=z[i+j-1]/100000000,z[i+j-1]%=100000000; z.cnt=x.cnt+y.cnt; if(!z[z.cnt]) --z.cnt; x=z; } ostream& operator << (ostream& os,abcd &x) { int i; printf("%lld",x[x.cnt]); for(i=x.cnt-1;i;i--) printf("%08lld",x[i]); return os; } int n,m,remain,cnt[M],stack[M],top; void Decomposition(int x,int y) { int i; for(i=2;i*i<=x;i++) while(x%i==0) cnt[i]+=y,x/=i; if(x^1) cnt[x]+=y; } void Quick_Power(int i,int y) { abcd x(i); while(y) { if(y&1)ans*=x; x*=x; y>>=1; } } int main() { int i,x; cin>>n;remain=n-2; for(i=1;i<=n;i++) { scanf("%d",&x); if(x==-1) ++m; else if(x>1) stack[++top]=x-1,remain-=x-1; } for(i=2;i<=n-2;i++) Decomposition(i,1); while(top) { for(i=2;i<=stack[top];i++) Decomposition(i,-1); stack[top--]=0; } for(i=2;i<=remain;i++) Decomposition(i,-1); Decomposition(m,remain); for(i=1;i<=n;i++) if(cnt[i]) Quick_Power(i,cnt[i]); cout<<ans<<endl; }