【prufer编码+组合数学】BZOJ1005 [HNOI2008]明明的烦恼
Description
自从明明学了树的结构,就对奇怪的树产生了兴趣...... 给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树?
Solution
这道题就是树的计数加强版,多了不要求的情况。
对于已限制的情况,就是C(n-2,t)*可重复元素的公式,考虑其他不限制的元素,再*(n-t)^(n-2-sum),t为已限制点个数,sum为已限制度数。
大概就是这个意思,计算要用分解质因数+高精度,具体细节自己推一推。
Code
因为是高精乘低精,高精度很好打。
1A十分感动,感觉最近打代码没以前那么无脑了。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 using namespace std; 5 const int maxn=5e3+5; 6 7 int dy[maxn],pri[maxn],tot[maxn],cnt; 8 int a[maxn],d[maxn],n,t,len; 9 10 int getpri(){ 11 for(int i=2;i<=n;i++){ 12 if(!dy[i]) pri[++cnt]=i,dy[i]=cnt; 13 for(int j=1;j<=cnt&&pri[j]*i<=n;j++){ 14 dy[pri[j]*i]=j; 15 if(i%pri[j]==0) break; 16 } 17 } 18 } 19 20 int add(int x,int k){ 21 while(x!=1){ 22 tot[dy[x]]+=k; 23 x/=pri[dy[x]]; 24 } 25 } 26 27 int mul(int x){ 28 for(int i=1;i<=len;i++) a[i]*=x; 29 for(int i=1;i<=len;i++) if(a[i]>=10){ 30 if(i==len) len++; 31 a[i+1]+=a[i]/10; 32 a[i]%=10; 33 } 34 } 35 36 int main(){ 37 int sum=0; 38 scanf("%d",&n); 39 for(int i=1;i<=n;i++){ 40 scanf("%d",&d[i]); 41 if(d[i]!=-1) sum+=d[i]-1; 42 } 43 if(sum>n-2){ 44 printf("0\n"); 45 return 0; 46 } 47 if(n==1){ 48 printf("1\n"); 49 return 0; 50 } 51 52 for(int i=1;i<=n;i++){ 53 if(!d[i]){ 54 printf("0\n"); 55 return 0; 56 } 57 if(d[i]!=-1) t++; 58 } 59 60 getpri(); 61 for(int i=1;i<=n-2;i++) add(i,1); 62 for(int i=1;i<=n-2-sum;i++) add(n-t,1); 63 for(int i=1;i<=n-2-sum;i++) add(i,-1); 64 for(int i=1;i<=n;i++) 65 for(int j=1;j<d[i];j++) add(j,-1); 66 67 len=a[1]=1; 68 for(int i=1;i<=cnt;i++) 69 for(int j=1;j<=tot[i];j++) mul(pri[i]); 70 71 for(int i=len;i>=1;i--) 72 printf("%d",a[i]); 73 return 0; 74 }