今天是一个%大佬的好日子,感觉大佬遍地走,只有我最弱……
这题是一道非常难的题目,虽然是“【BZOJ】1005 [HNOI2008] 明明的烦恼”的简化版,但是仍然是一道非常难的数学题。(至少我是这么认为的)
首先,在做这题之前,我们需要了解prufer编码。百度百科传送门,个人认为百度百科已经讲的很清楚了,就不在这里再做赘述了。
然后我们就要考虑如何把prufer编码转化成组合数学来解这道题。
在prufer编码里,出现k次的节点在树上的度为k+1,因此我们可以得到ans=(n-2)!/((d1-1)!(d2-1)!...(dn-1)!)。
虽然题目保证ans在10^17范围内,但是在中途做阶乘的时候可能会爆long long,因此我们需要分解质因数,把乘除法转化为质因数指数的加减法。
另外,注意无法生成树的情况,即d[i]==0且n>1时或Σ(d[i]-1)!=n-2时,需要输出0。
至此,我们就彻底解决了这道难题啦。
附上AC代码:
#include <cstdio>
#include <cmath>
using namespace std;
int n,x,sum,c[160];
long long ans=1;
inline void change(int x,int y){
for (int i=2; i*i<=x; ++i)
while (x%i==0) c[i]+=y,x/=i;
if (x^1) c[x]+=y;
return;
}
inline long long calc(int x,int y){
int ans=1;
while (y){
if (y&1) ans*=x;
x*=x,y>>=1;
}
return ans;
}
int main(void){
scanf("%d",&n);
for (int i=2; i<=n-2; ++i) change(i,1);
for (int i=1; i<=n; ++i){
scanf("%d",&x),sum+=x-1;
if (!x&&n>1) goto Z;
for (int j=2; j<=x-1; ++j) change(j,-1);
}
if (sum!=n-2) goto Z;
for (int i=2; i<=n-2; ++i) if (c[i]) ans*=calc(i,c[i]);
printf("%lld",ans);
return 0;
Z: puts("0");
return 0;
}