关闭

[HNOI2008]明明的烦恼 (Prüfer编码+质因数分解计算两个阶乘的商)

标签: HNOI2008明明的烦恼Prfer编码质因数分解
333人阅读 评论(0) 收藏 举报
分类:

1005: [HNOI2008]明明的烦恼

Time Limit: 1 Sec  Memory Limit: 162 MB
Submit: 3396  Solved: 1346
[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

Sample Output

2

HINT

两棵树分别为1-2-3;1-3-2

Source

[Submit][Status][Discuss]

HOME Back


以下为转载:http://www.cnblogs.com/noip/archive/2013/03/10/2952520.html

该题运用到了树的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 //模仿CLJ大神写的STL,并学会了各种写代码技巧,长见识了! 
  2 #include<iostream>
  3 #include<cstring>
  4 #include<string.h>
  5 #include<algorithm>
  6 #include<cmath>
  7 #include<vector>
  8 using namespace std;
  9 
 10 int n,nolimit=0,tot=0,ans[10005]={0};
 11 
 12 vector<int> prime;
 13 
 14 typedef struct{
 15        int h[400];
 16        void Init(){memset(h,0,sizeof(h));}
 17        
 18        void mul(int x){
 19             for(int i=0;i<prime.size();++i)
 20             while(x%prime[i]==0)
 21             {h[i]++;x/=prime[i];}
 22             }
 23        void div(int x){
 24             for(int i=0;i<prime.size();++i)
 25             while(x%prime[i]==0)
 26             {h[i]--;x/=prime[i];}
 27             }
 28        
 29        }typenum;typenum sum;
 30        
 31 bool Isprime(int x){
 32      int i;
 33      for(i=2;i<=sqrt(x);++i)
 34      if(x%i==0) return 0;
 35      return 1;
 36      }
 37 
 38 void Makeprime(){
 39      for(int i=2;i<=n;++i)
 40      if(Isprime(i)) prime.push_back(i);
 41      return ;
 42      }
 43 
 44 void Init(){
 45      cin>>n;
 46      Makeprime();
 47    //  for(int i=0;i<prime.size();++i)
 48    //  cout<<prime[i]<<"  ";cout<<endl; 
 49      sum.Init();
 50    //  for(int i=0;i<400;++i)
 51    //  cout<<sum.h[i]<<"  ";
 52      int d;
 53      for(int i=1;i<=n;++i)
 54      {
 55        cin>>d;
 56        
 57        for(int i=1;i<d;++i)
 58        sum.div(i);
 59        if(d==-1)nolimit++;///注意 
 60        else tot+=d-1;////注意 
 61              }
 62      }
 63 
 64 void Work(){
 65      if(tot>n-2||(tot!=n-2&&nolimit==0)) {cout<<0<<endl;return ;}
 66      
 67      for(int i=1;i<=n-2;++i)
 68      sum.mul(i);
 69      
 70      for(int i=1;i<=n-2-tot;++i)
 71      sum.div(i);
 72      
 73      for(int i=1;i<=n-2-tot;++i)
 74      sum.mul(nolimit);
 75      
 76      ans[0]=1;
 77      for(int i=0;i<prime.size();++i)
 78      while(sum.h[i]>0)
 79      {
 80        sum.h[i]--;
 81        for(int j=0;j<10000;++j)
 82        ans[j]*=prime[i];
 83        
 84        for(int j=0;j<10000;++j)
 85        if(ans[j]>9)
 86        {ans[j+1]+=ans[j]/10;ans[j]%=10;}
 87                    }
 88      
 89      int i=10000;
 90      while(ans[i]==0) i--;
 91      while(i>=0) cout<<ans[i--];
 92      cout<<endl;
 93      }
 94 
 95 int main()
 96 {
 97     Init();
 98     Work();
 99   //  Print();
100    // system("pause");
101     return 0;
102     }
复制代码
 
n!质因数特殊分解:
复制代码
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<string.h>
 5 #include<cstdlib>
 6 #include<cmath>
 7 #include<fstream>
 8 using namespace std;
 9 //ifstream cin("cin.in");
10  
11 int n,m=0,tot=0,fenmu[1005]={0},fenzi[1005]={0},prime[1005]={0};
12 int ans[10005]={0};
13  
14 void Fenjie(int zz,int a[]){
15      int sum;
16      for(int i=2;i<=zz;++i)
17      if(prime[i])
18      {
19        sum=i;
20        while(sum<=zz)
21        {a[i]+=zz/sum;sum*=i;}
22              }
23      return ;
24      }
25  
26 void Buildprime(){
27      int i,j;
28      for(i=2;i<=1000;++i)
29      {
30        for(j=2;j<=sqrt(i);++j)
31        if(i%j==0) break;     
32        if(j>sqrt(i)) prime[i]=1;//,cout<<i<<"  ";
33              }//cout<<endl;
34      }
35  
36 int main()
37 {
38     cin>>n;
39      
40     Buildprime();
41  
42     for(int i=1;i<=n;++i)
43     {
44       int d;
45       cin>>d;
46       if(d==-1) {m++;continue;}
47       if(d>1) Fenjie(d-1,fenmu);
48       tot+=d-1;
49             }
50      
51     Fenjie(n-2-tot,fenmu);
52     Fenjie(n-2,fenzi);
53      
54     for(int i=1;i<=1000;++i)
55     fenzi[i]-=fenmu[i];
56      
57     ans[0]=1;
58      
59     for(int i=1;i<=1000;++i)
60     while(fenzi[i]>0)
61     {
62       fenzi[i]--;
63       for(int j=0;j<=10000;++j)
64       ans[j]*=i;
65       for(int j=0;j<=10000;++j)
66       if(ans[j]>9)
67       {ans[j+1]+=ans[j]/10;ans[j]%=10;}       
68                      }
69      
70     if(m>0)
71     for(int i=1;i<=n-2-tot;++i)
72     {
73       for(int j=0;j<=10000;++j)
74       ans[j]*=m;
75       for(int j=0;j<=10000;++j)
76       if(ans[j]>9)
77       {
78         ans[j+1]+=ans[j]/10;
79         ans[j]%=10;
80               }
81             }
82      
83     if(tot>n-2||(tot<n-2&&m==0)) {cout<<0<<endl;return 0;}
84      
85     int i=10000;
86     while(ans[i]==0) i--;
87    // if(i<=0) cout<<ans[0];
88     while(i>=0) cout<<ans[i--];
89     cout<<endl;
90      
91    // system("pause");
92     return 0;
93      
94     } 
复制代码
 
最后还要吐槽一句:八中OJ评测不给力啊,就因为ans数组开小了害我检查了一天的WA。

以下为我自己写的代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ms(a) memset(a,0,sizeof(a))
using namespace std;

const int maxn=1e3;
const int len=1e4;
int prime[maxn];
bool flag[maxn];
int n,tot,m;
int a[maxn],b[maxn],d[maxn];
int ans[maxn];

void get_prime()
{
  int i,j;ms(flag),prime[0]=0;
  for(i=2;i<maxn;i++)
    {
      if(!flag[i])prime[++prime[0]]=i;
      for(j=1;j<=prime[0] && i*prime[j]<maxn;j++)
        {
          flag[i*prime[j]]=1;
          if(i%prime[j]==0)break;
		}
	}
}

void get_fact(int l,int r)
{
  int x,i,j;
  for(x=l;l<=r;x=(++l))
    for(i=1;x>1;i++)if(x%prime[i]==0)
      {
	    for(j=0;x%prime[i]==0;x/=prime[i])j++;
	    b[i]+=j;
	  }
}

void resolve()
{
  int i,j; ms(a),ms(b);
  for(i=2;i<=d[0];i++)
    {
      if(d[i]!=d[i-1])get_fact(d[i-1]+1,d[i]);
      for(j=1;j<=prime[0];j++)a[j]+=b[j];
	}
  get_fact(d[d[0]]+1,n-2);
  for(j=1;j<=prime[0];j++)a[j]=b[j]-a[j];
  ms(b),get_fact(m,m);
  for(j=1;j<=prime[0];j++)a[j]+=b[j]*(n-2-tot);
}

void multi(int x)
{
  int i,last=0;
  for(i=1;i<=ans[0];i++)
    {
      ans[i]=ans[i]*x+last;
      last=ans[i]/len,ans[i]%=len;
	}
  if(last)ans[++ans[0]]=last;
}

int main()
{
  //freopen("1.in","r",stdin);
  
  int i,j;
  get_prime(),scanf("%d",&n);
  for(d[0]=tot=0,i=1;i<=n;i++)
    {
      scanf("%d",&j);
      if(j!=-1)tot+=(d[++d[0]]=j-1);
	}
  m=n-d[0],d[++d[0]]=0,d[++d[0]]=n-2-tot;
  sort(d+1,d+d[0]+1);

  resolve();
  
  ans[0]=1,ans[1]=1;
  for(i=1;i<=prime[0];i++)
    for(j=1;j<=a[i];j++)
      multi(prime[i]);
  printf("%d",ans[ans[0]]);
  for(i=ans[0]-1;i>=1;i--)
    printf("%04d",ans[i]);
  return 0;
}




0
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

java算法——分解质因数

//分解质因数 //每个合数都可以写成几个质数相乘的形式。其中每个质数都是这个合数的因数,叫做这个合数的分解质因数。 package com.suanfa; import java.util....
  • tingzhiyi
  • tingzhiyi
  • 2016-07-28 21:35
  • 7853

矩阵乘法的算法实现 [转载]

一般矩阵乘法算法: 原理:矩阵相乘最重要的方法是一般矩阵乘积。它只有在第一个矩阵的栏数(column)和第二个矩阵的列数(row)相同时才有定义。一般单指矩阵乘积时,指的便是一般矩阵乘积。若A为m×...
  • zhengqijun_
  • zhengqijun_
  • 2016-11-26 23:04
  • 3463

计算n阶乘中尾部零的个数

写在前面本来觉得问题挺容易的,不打算记录,谁知道一不小心,还真没做出来。最终凭借“朴实”的算法思想解决了问题,但是其中的曲折还真是汗颜。科学的思维指导确实必不可少,“野路子”的朴素的战斗理论不论是效率...
  • surp2011
  • surp2011
  • 2016-04-16 15:17
  • 5090

[bzoj1005]:[HNOI2008]明明的烦恼(prufer序列+质因数分解+高精乘)

传送门 首先,原来我写过这个题,然而我用的别人的高精板子,然后就没有然后了。 这个故事告诉我们,千万不要用别人的板子。 好,我们开始。 首先大家都知道prufer序列这个东西吧 (没看过...
  • stone41123
  • stone41123
  • 2017-11-22 15:19
  • 65

Prüfer编码与Cayley公式

Prüfer编码与Cayley公式 过n个有标志顶点的树的数目等于n^(n-2) 今天遇到一个问题:在一个n阶完全图的所有生成树的数量为n的n-2次方,想了好久也没有想出来,还...
  • Justesss
  • Justesss
  • 2014-07-25 21:57
  • 576

ZOJ 3604 Tunnel Network [Prüfer编码与Cayley公式] 【树】

!!!! 摘自题目链接 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3604———————————————————————...
  • qq_33184171
  • qq_33184171
  • 2017-02-07 18:40
  • 172

经典证明:Prüfer编码与Cayley公式

原文链接:http://www.matrix67.com/blog/archives/682 经典证明:Prüfer编码与Cayley公式        Cayley公式是说,一个完全图K_n有n...
  • yuyanggo
  • yuyanggo
  • 2015-11-20 20:34
  • 573

Prüfer编码与Cayley公式

——转自matrix67     Cayley公式是说,一个完全图K_n有n^(n-2)棵生成树,换句话说n个节点的带标号的无根树有n^(n-2)个。今天我学到了Cayley公式的一个非...
  • zz_1215
  • zz_1215
  • 2012-08-31 20:09
  • 429

[BZOJ1005]HNOI2008 明明的烦恼|prufer编码|排列组合

太可怕了,HN怎这么喜欢数学题,这几天被数学题虐成狗。。这题有一个什么prufer编码的东西,就是一棵树和一个prufer编码是唯一对应的,也就是把树的可能方案转化成一个数列的转化方案来做,然后就排列...
  • Tag_king
  • Tag_king
  • 2015-04-17 09:24
  • 237

HYSBZ/BZOJ 1005 [HNOI2008] 明明的烦恼 - Prufer编码&组合数学&高精度 此乃神题!

题目描述分析&Solution: hzw大神的blog JMJST大神的blog Matrix67对Prufer编码的理解 再结合题解,才终于理解怎么回事,大赞hzw大神的blog。#include ...
  • yuanxinyu402
  • yuanxinyu402
  • 2016-02-02 20:56
  • 377
    个人资料
    • 访问:270075次
    • 积分:6252
    • 等级:
    • 排名:第4565名
    • 原创:353篇
    • 转载:59篇
    • 译文:0篇
    • 评论:26条
    最新评论