【BZOJ1005/1211】[HNOI2008]明明的烦恼/[HNOI2004]树的计数 Prufer序列+高精度
【BZOJ1005】[HNOI2008]明明的烦恼
Description
自从明明学了树的结构,就对奇怪的树产生了兴趣......给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树?
Input
第一行为N(0 < N < = 1000),
接下来N行,第i+1行给出第i个节点的度数Di,如果对度数不要求,则输入-1
Output
一个整数,表示不同的满足要求的树的个数,无解输出0
Sample Input
3
1
-1
-1
1
-1
-1
Sample Output
2
HINT
两棵树分别为1-2-3;1-3-2
题解:每个点在Prufer序列中出现的次数=这个点的度数-1,所以我们令m为已经确定度数的点的数量,$tot=\sum D[i]-1$,那么其他位置可以随便选,方案数为$(n-2-tot)^{n-m}$。然后这些已经确定的位置可以随便排列,方案数为$C_{n-2}^{tot} \times C_{tot}^{d1} \times C_{tot-d1}^{d2} \times ...$。
但显然结果是爆longlong的,不过可以确定答案一定是个整数。为了避免高精度除法,我们可以将分子和分母都拆成质数的几次方形式,然后分子分母做减法,最后用高精度将这些质数乘起来就行了。
1211那题需要特判:tot!=n-2输出0;n>1且di=0输出0;di>=n输出0。
#include <cstdio> #include <iostream> #include <cstring> using namespace std; const int maxn=1010; int n,m,tot,num; int pri[maxn],np[maxn],lp[maxn],s[maxn],d[maxn],B[maxn]; struct Cbig { int a[10010],len; Cbig() {memset(a,0,sizeof(a)),len=1;} int & operator [] (int b) {return a[b];} void operator *= (const int &b) { for(int i=1;i<=len;i++) a[i]*=b; for(int i=1;i<=len;i++) a[i+1]+=a[i]/1000,a[i]%=1000; while(a[len+1]) len++; while(len&&!a[len]) len--; } }ans; inline void add(int x,int v) { while(x!=1) s[lp[x]]+=v,x/=pri[lp[x]]; } void init() { int i,j; for(i=2;i<=n;i++) { if(!np[i]) pri[++num]=i,lp[i]=num; for(j=1;j<=num&&i*pri[j]<=n;j++) { np[i*pri[j]]=1,lp[i*pri[j]]=j; if(i%pri[j]==0) break; } } } inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<'0'||gc>'9') {if(gc=='-')f=-f; gc=getchar();} while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar(); return ret*f; } int main() { n=rd(),tot=0,init(); int i; for(i=1;i<=n;i++) { d[i]=rd(); if(d[i]==0) { printf("0"); return 0; } if(d[i]>0) d[i]--,m++,tot+=d[i]; } if(tot>n-2) { printf("0"); return 0; } ans.a[1]=ans.len=1; for(i=1;i<=n-2-tot;i++) ans*=(n-m); B[n-2]++,B[n-2-tot]--; for(i=1;i<=n;i++) if(d[i]>=0) B[d[i]]--,tot-=d[i]; for(i=n-2;i>=1;i--) B[i]+=B[i+1],add(i,B[i]); for(i=1;i<=n-2;i++) while(s[i]--) ans*=pri[i]; printf("%d",ans.a[ans.len]); for(i=ans.len-1;i>=1;i--) printf("%03d",ans.a[i]); return 0; }//5 1 1 -1 -1 -1
#include <cstdio> #include <iostream> #include <cstring> using namespace std; const int maxn=1010; int n,tot,num; int pri[maxn],np[maxn],lp[maxn],s[maxn],d[maxn],B[maxn]; struct Cbig { int a[10010],len; Cbig() {memset(a,0,sizeof(a)),len=1;} int & operator [] (int b) {return a[b];} void operator *= (const int &b) { for(int i=1;i<=len;i++) a[i]*=b; for(int i=1;i<=len;i++) a[i+1]+=a[i]/1000,a[i]%=1000; while(a[len+1]) len++; while(len&&!a[len]) len--; } }ans; inline void add(int x,int v) { while(x!=1) s[lp[x]]+=v,x/=pri[lp[x]]; } void init() { int i,j; for(i=2;i<=n;i++) { if(!np[i]) pri[++num]=i,lp[i]=num; for(j=1;j<=num&&i*pri[j]<=n;j++) { np[i*pri[j]]=1,lp[i*pri[j]]=j; if(i%pri[j]==0) break; } } } inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<'0'||gc>'9') {if(gc=='-')f=-f; gc=getchar();} while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar(); return ret*f; } int main() { n=rd(),tot=0,init(); int i; for(i=1;i<=n;i++) { d[i]=rd(); if((n!=1&&d[i]==0)||d[i]>=n) { printf("0"); return 0; } d[i]--,tot+=d[i]; } if(tot!=n-2) { printf("0"); return 0; } ans.a[1]=ans.len=1; B[n-2]++,B[n-2-tot]--; for(i=1;i<=n;i++) if(d[i]>=0) B[d[i]]--,tot-=d[i]; for(i=n-2;i>=1;i--) B[i]+=B[i+1],add(i,B[i]); for(i=1;i<=n-2;i++) while(s[i]--) ans*=pri[i]; printf("%d",ans.a[ans.len]); for(i=ans.len-1;i>=1;i--) printf("%03d",ans.a[i]); return 0; }//5 1 1 -1 -1 -1
| 欢迎来原网站坐坐! >原文链接<