【bzoj1005】[HNOI2008]明明的烦恼
1005: [HNOI2008]明明的烦恼
Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 4175 Solved: 1660
[Submit][Status][Discuss]
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)树的prufer编码的实现
不断 删除树中度数为1的最小序号的点,并输出与其相连的节点的序号 直至树中只有两个节点
(2)通过观察我们可以发现
任意一棵n节点的树都可唯一的用长度为n-2的prufer编码表示
度数为m的节点的序号在prufer编码中出现的次数为m-1
(3)怎样将prufer编码还原为一棵树??
从prufer编码的最前端开始扫描节点,设该节点序号为 u ,寻找不在prufer编码的最小序号且没有被标记的节点 v ,连接 u,v,并标记v,将u从prufer编码中删除。扫描下一节点。
该题需要将树转化为prufer编码:
n为树的节点数,d[ ]为各节点的度数,m为无限制度数的节点数。
则
所以要求在n-2大小的数组中插入tot各序号,共有种插法;
在tot各序号排列中,插第一个节点的方法有种插法;
插第二个节点的方法有种插法;
………
另外还有m各节点无度数限制,所以它们可任意排列在剩余的n-2-tot的空间中,排列方法总数为;
根据乘法原理:
然后就要高精度了…..但高精度除法太麻烦了,显而易见的排列组合一定是整数,所以可以进行质因数分解,再做一下相加减。
关于n!质因数分解有两种方法,第一种暴力分解,这里着重讲第二种。
若p为质数,则n!可分解为 一个数*,其中且 <n
所以
——转自怡红公子
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<cmath> 6 #include<ctime> 7 #include<algorithm> 8 using namespace std; 9 #define mod 1000000 10 int n,m,tot,cnt,len=1,d[1010],pri[1010],num[1010],f[1010],ans[1010]; 11 inline int read() 12 { 13 int x=0,f=1; char ch=getchar(); 14 while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();} 15 while(isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();} 16 return x*f; 17 } 18 void gets()//线性筛素数 19 { 20 memset(f,1,sizeof(f)); 21 for(int i=2;i<=1000;i++) 22 { 23 if(f[i]) pri[++cnt]=i; 24 for(int j=1;j<=cnt;j++) 25 { 26 if(pri[j]*i>1000)break; 27 f[pri[j]*i]=0; 28 if(i%pri[j]==0)break; 29 } 30 } 31 } 32 void solve(int x,int f)//暴力分解x 33 { 34 for(int i=1;i<=x;i++) 35 { 36 int k=i; 37 for(int j=1;j<=cnt;j++) 38 { 39 if(k<=1) break; 40 while(k%pri[j]==0) 41 {num[j]+=f; k/=pri[j];} 42 } 43 } 44 } 45 void mul(int x)//100万进制高精乘 46 { 47 for(int i=1;i<=len;i++) ans[i]*=x; 48 for(int i=1;i<=len;i++) 49 { 50 ans[i+1]+=ans[i]/mod; 51 ans[i]%=mod; 52 } 53 while(ans[len+1]) 54 {len++; ans[len+1]=ans[len]/mod; ans[len]%=mod;} 55 } 56 void print()//输出高精度数 57 { 58 for(int i=len;i;i--) 59 if(i==len) printf("%d",ans[i]); 60 else printf("%06d",ans[i]); 61 } 62 int main() 63 { 64 n=read(); ans[1]=1; 65 gets();//读素数表 66 if(n==1) //特判 67 { 68 int x=read(); 69 if(!x) printf("1\n"); 70 else printf("0\n"); 71 return 0; 72 } 73 for(int i=1;i<=n;i++) 74 { 75 d[i]=read(); 76 if(!d[i]) {printf("0\n"); return 0;} 77 if(d[i]==-1) m++; 78 else d[i]--,tot+=d[i]; 79 } 80 if(tot>n-2) {printf("0\n"); return 0;} 81 solve(n-2,1); 82 solve(n-2-tot,-1); 83 for(int i=1;i<=n;i++) 84 if(d[i]) solve(d[i],-1); 85 for(int i=1;i<=cnt;i++) 86 while(num[i]--) 87 mul(pri[i]); 88 for(int i=1;i<=n-2-tot;i++) 89 mul(m); 90 print(); 91 return 0; 92 }