两道题目大意都是根据每个点的度数来构建一棵无根树来确定有多少种构建方法
这里构建无根树要用到的是prufer序列的知识
先很无耻地抄袭了一段百度百科中的prufer序列的知识:
将树转化成Prufer数列的方法
一种生成Prufer序列的方法是迭代删点,直到原图仅剩两个点。对于一棵顶点已经经过编号的树T,顶点的编号为{1,2,...,n},在第i步时,移去所有叶子节点(度为1的顶点)中标号最小的顶点和相连的边,并把与它相邻的点的编号加入Prufer序列中,重复以上步骤直到原图仅剩2个顶点。
例子
以右边的树为例子,首先在所有叶子节点中编号最小的点是2,和它相邻的点的编号是3,将3加入序列并删除编号为2的点。接下来删除的点是4,5被加入序列,然后删除5,1被加入序列,1被删除,3被加入序列,此时原图仅剩两个点(即3和6),Prufer序列构建完成,为{3,5,1,3}
将Prufer数列转化成树的方法
设{a1,a2,..an-2}为一棵有n个节点的树的Prufer序列,另建一个集合G含有元素{1..n},找出集合中最小的未在Prufer序列中出现过的数,将该点与Prufer序列中首项连一条边,并将该点和Prufer序列首项删除,重复操作n-2次,将集合中剩余的两个点之间连边即可。
例子
仍为上面的树,Prufer序列为{3,5,1,3},开始时G={1,2,3,4,5,6},未出现的编号最小的点是2,将2和3连边,并删去Prufer序列首项和G中的2。接下来连的边为{4,5},{1,5},{1,3},此时集合G中仅剩3和6,在3和6之间连边,原树恢复
这样很容易可以发现通过构建n-2个数的数列就可以得到唯一的一棵无根树,当然如果要求根的不同的话,那么在种数的基础上乘以n就行了嘛,反正每个点都是能作为根的
仔细观察数列很容易发现,构建数列的过程中每次删除的都是度数为1的点,放入父亲,那就有点像拓扑排序的过程了
而最后留了两个数,也可以理解为从prufer序列中删除,那么删除的点都减少了一个度数,其他度数有多少都放进了prufer序列中
那么就说明prufer序列中某一个点出现的次数就是它本身的度数减1
再从父亲到儿子的角度思考的话,因为prufer序列有n-2个点,但树有n-1条边,最后一条边也删去的话,最后加入序列末尾的必然是最大值n
那么这个时候我们可以把整个图看作是以n为根的树,出现在prufer序列中的点的次数就是它在树上含有的儿子个数
对于这两道题目的计算来说的话,就相当于将能构成树的这些树进行全排列,得到排列的方法数就行了
(n-2)! / (w[1]!*w[2]!*w[3]!...) w[i]表示 i 的度数-1
这种题目需要注意到无法构建树的情况即可
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <vector> 5 using namespace std; 6 #define pii pair<int,int> 7 int n , w[200] , num[200]; 8 vector<pii> v[200]; 9 10 void solve(int m) 11 { 12 if(m==1) return; 13 int tmp = m; 14 for(int i=2 ; i<=m ; i++){ 15 if(m%i==0){ 16 int cnt = 0; 17 while(m%i==0){ 18 m/=i; 19 cnt++; 20 } 21 v[tmp].push_back(make_pair(i , cnt)); 22 } 23 } 24 if(m>1) v[tmp].push_back(make_pair(m , 1)); 25 // for(int i=0 ; i<v[tmp].size() ; i++) cout<<v[tmp][i].first<<" "<<v[tmp][i].second<<endl; 26 } 27 void init() 28 { 29 for(int i=1 ; i<=150 ; i++) solve(i); 30 } 31 void update(int k , int flag) 32 { 33 for(int i=0 ; i<v[k].size() ; i++){ 34 pii u=v[k][i]; 35 num[u.first]+=u.second*flag; 36 } 37 } 38 void mul(long long &ans , int k , int tim) 39 { 40 for(int i=1 ; i<=tim ; i++) ans=ans*k; 41 } 42 int main() 43 { 44 // freopen("Sweet.in" , "r" , stdin); 45 init(); 46 while(~scanf("%d" , &n)){ 47 memset(num , 0 , sizeof(num)); 48 int sum=0 , flag=true; 49 for(int i=1 ; i<=n ; i++){ 50 scanf("%d" , &w[i]); 51 if(w[i]==0) flag = false; 52 sum+=w[i]-1; 53 for(int j=1 ; j<=w[i]-1 ; j++) update(j , -1); 54 } 55 if(n==1 && w[1]==0){ 56 cout<<1<<endl; 57 continue; 58 } 59 if(sum!=n-2 || n==1 || flag==false){ 60 cout<<0<<endl; 61 continue; 62 } 63 for(int i=1 ; i<=n-2 ; i++) update(i , 1); 64 long long ans = 1; 65 for(int i=1 ; i<=n-2 ; i++){ 66 if(num[i]) mul(ans , i , num[i]); 67 } 68 cout<<ans<<endl; 69 } 70 return 0; 71 }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 using namespace std; 5 #define N 1005 6 int n , a[N] , cnt[N]; 7 int ans[N*10] , l1; 8 int mul[N] , l2; 9 int tmp[N*10]; 10 11 void fenjie(int x , int flag) 12 { 13 for(int i=2 ; i*i<=x ; i++){ 14 while(x%i==0){ 15 cnt[i]+=flag; 16 x/=i; 17 } 18 } 19 if(x>1) cnt[x]+=flag; 20 } 21 22 void solveC(int a , int b) //C(n-2 , sum) 23 { 24 for(int i=1 ; i<=b ; i++) fenjie(i , -1); 25 for(int i=a ; i>a-b ; i--) fenjie(i , 1); 26 } 27 28 void change(int x) 29 { 30 l2 = 0; 31 while(x){ 32 mul[l2++] = x%10; 33 x/=10; 34 } 35 } 36 37 void cal() 38 { 39 memset(tmp , 0 , sizeof(tmp)); 40 int len = 0; 41 for(int i=0 ; i<l2 ; i++){ 42 for(int j=0 ; j<l1 ; j++){ 43 int cur = i+j; 44 len = max(len , cur+1); 45 tmp[cur] += ans[j]*mul[i]; 46 } 47 } 48 for(int i=0 ; i<len ; i++){ 49 if(tmp[i]>=10){ 50 len= max(len , i+2); 51 tmp[i+1] += tmp[i]/10; 52 tmp[i] %= 10; 53 } 54 } 55 for(int i=0 ; i<len ; i++) ans[i] = tmp[i] , l1 = len; 56 } 57 58 void print() 59 { 60 for(int i=l1-1 ; i>=0 ; i--) printf("%d" , ans[i]); 61 printf("\n"); 62 } 63 int main() 64 { 65 // freopen("a.in" , "r" , stdin); 66 while(~scanf("%d" , &n)){ 67 memset(cnt , 0 , sizeof(cnt)); 68 int w = 0 , sum = 0; 69 bool flag = true; 70 for(int i=1 ; i<=n ; i++){ 71 scanf("%d" , &a[i]); 72 if(a[i]==0) flag=false; 73 if(a[i]<0) w++; 74 else{ 75 sum+=a[i]-1; 76 for(int j=1 ; j<=a[i]-1 ; j++) fenjie(j , -1); 77 } 78 } 79 if(n==1 && a[1]==0){ 80 cout<<1<<endl; 81 continue; 82 } 83 solveC(n-2 , sum); 84 if(n==1 || sum>n-2 || flag==false) cout<<0<<endl; 85 else{ 86 for(int i=1 ; i<=sum ; i++) fenjie(i, 1); 87 int cnt = n-2-sum; 88 for(int i=1 ; i<=cnt ; i++) fenjie(w , 1); 89 } 90 // for(int i=1 ; i<=10 ; i++) cnt[i]++; 91 ans[0] = 1 , l1=1; 92 for(int i=1 ; i<=n ; i++){ 93 if(cnt[i]){ 94 change(i); 95 for(int j=1 ; j<=cnt[i] ; j++){ 96 cal(); 97 } 98 } 99 } 100 print(); 101 } 102 return 0; 103 }