• [HNOI2008]明明的烦恼


    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

     
     
     
     
    该题运用到了树的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。
  • 相关阅读:
    PHP、asp、aspx、JSP一句话
    Linux 修改时区(PDT修改为CST)
    m0n0防火墙安装配置方法
    kali 使用John破解zip压缩包的密码
    Linux 挂载windows共享文件夹
    博客园添加粒子特效
    wpscan 更新超时报错
    kali 攻击wordpress + trunkey linux wordpress 安装方法
    Kali和Metasploitable2的网络配置
    设计模式之观察者
  • 原文地址:https://www.cnblogs.com/noip/p/2952520.html
Copyright © 2020-2023  润新知