• 【BZOJ】【1211】【HNOI2004】树的计数


    Prufer序列+组合数学


      嗯哼~给定每个点的度数!求树的种数!那么很自然的就想到是用prufer序列啦~(不知道prufer序列的……自己再找找资料吧,这里就不放了,可以去做一下BZOJ1005明明的烦恼)

      那么我们令每个点的度数v[i]-1,得到每个节点在prufer序中的出现次数!

      现在就是求这个prufer序有多少种了……有两种做法:

        1.多重集排列数:n个元素,每种元素有a[i]个,求全排列的方案数,自己随便yy一下就可以得到$$ans=frac{n!}{prod (a[i]!)}$$

         意义就是:n个人全排列的数目是N!,然而对于第一种人,这a[1]个人站的顺序不同也只算一种,所以要除以(a[1]!),以此类推,得到上述表达式。

     1 /**************************************************************
     2     Problem: 1211
     3     User: ProgrammingApe
     4     Language: C++
     5     Result: Accepted
     6     Time:0 ms
     7     Memory:1288 kb
     8 ****************************************************************/
     9  
    10 //BZOJ 1211
    11 #include<cmath>
    12 #include<cstdio>
    13 #include<cstring>
    14 #include<cstdlib>
    15 #include<iostream>
    16 #include<algorithm>
    17 #define rep(i,n) for(int i=0;i<n;++i)
    18 #define F(i,j,n) for(int i=j;i<=n;++i)
    19 #define D(i,j,n) for(int i=j;i>=n;--i)
    20 #define pb push_back
    21 using namespace std;
    22 typedef long long LL;
    23 inline LL getint(){
    24     LL r=1,v=0; char ch=getchar();
    25     for(;!isdigit(ch);ch=getchar()) if (ch=='-') r=-1;
    26     for(; isdigit(ch);ch=getchar()) v=v*10-'0'+ch;
    27     return r*v;
    28 }
    29 const int N=401;
    30 /*******************template********************/
    31 LL n,m,a[N],ans,fac[N],prime[N],tot;
    32 LL b[N];
    33 bool vis[N];
    34 void ready(int n){
    35     F(i,2,n){
    36         if (!vis[i]) prime[++tot]=i;
    37         F(j,1,tot){
    38             if (i*prime[j]>n) break;
    39             vis[i*prime[j]]=1;
    40             if (i%prime[j]==0) break;
    41         }
    42     }
    43 }
    44 void add(int k,int v){
    45 //  printf("add %d %d
    ",k,v);
    46     F(i,2,k){
    47         int x=i;
    48         F(j,1,tot) while(x%prime[j]==0){
    49             x/=prime[j];
    50             b[j]+=v;
    51         }
    52     }
    53 }
    54 int main(){
    55 #ifndef ONLINE_JUDGE
    56     freopen("tree.in","r",stdin);
    57     freopen("tree.out","w",stdout);
    58 #endif
    59     ready(300);
    60     n=getint();
    61     if (n==1){
    62         a[1]=getint();
    63         if (a[1]!=0) {puts("0"); return 0;}
    64         else {puts("1"); return 0;}
    65     }
    66     F(i,1,n){
    67         a[i]=getint()-1;
    68         if (a[i]<0 || a[i]>n-1) {puts("0"); return 0;}
    69         m+=a[i];
    70     }
    71     if (m!=n-2){puts("0"); return 0;}
    72     add(m,1);
    73     F(i,1,n) add(a[i],-1);
    74     ans=1;
    75 
    76     F(i,1,tot) 
    77         F(j,1,b[i]) ans*=prime[i];
    78     printf("%lld
    ",ans);
    79     return 0;
    80 }
    View Code(多重集排列数)

        2.组合数:n个位置,拿出a[1]个放第一种元素,再从剩下的(n-a[1])个里面选出a[2]个……所以有$$ans=prod_{i=1}^{n} inom{a[i]}{n-sum_{j=0}^{i-1}(a[j])} $$

     1 /**************************************************************
     2     Problem: 1211
     3     User: ProgrammingApe
     4     Language: C++
     5     Result: Accepted
     6     Time:4 ms
     7     Memory:1288 kb
     8 ****************************************************************/
     9  
    10 //BZOJ 1211
    11 #include<cmath>
    12 #include<cstdio>
    13 #include<cstring>
    14 #include<cstdlib>
    15 #include<iostream>
    16 #include<algorithm>
    17 #define rep(i,n) for(int i=0;i<n;++i)
    18 #define F(i,j,n) for(int i=j;i<=n;++i)
    19 #define D(i,j,n) for(int i=j;i>=n;--i)
    20 #define pb push_back
    21 using namespace std;
    22 typedef long long LL;
    23 inline LL getint(){
    24     LL r=1,v=0; char ch=getchar();
    25     for(;!isdigit(ch);ch=getchar()) if (ch=='-') r=-1;
    26     for(; isdigit(ch);ch=getchar()) v=v*10-'0'+ch;
    27     return r*v;
    28 }
    29 const int N=401;
    30 /*******************template********************/
    31 LL n,m,a[N],ans,fac[N],prime[N],tot;
    32 LL b[N];
    33 bool vis[N];
    34 void ready(int n){
    35     F(i,2,n){
    36         if (!vis[i]) prime[++tot]=i;
    37         F(j,1,tot){
    38             if (i*prime[j]>n) break;
    39             vis[i*prime[j]]=1;
    40             if (i%prime[j]==0) break;
    41         }
    42     }
    43 }
    44 void add(int k,int v){
    45 //  printf("add %d %d
    ",k,v);
    46     F(i,2,k){
    47         int x=i;
    48         F(j,1,tot) while(x%prime[j]==0){
    49             x/=prime[j];
    50             b[j]+=v;
    51         }
    52     }
    53 }
    54 inline void C(int a,int b){ add(a,1); add(b,-1); add(a-b,-1);}
    55 int main(){
    56 #ifndef ONLINE_JUDGE
    57     freopen("tree.in","r",stdin);
    58     freopen("tree.out","w",stdout);
    59 #endif
    60     ready(300);
    61     n=getint();
    62     if (n==1){
    63         a[1]=getint();
    64         if (a[1]!=0) {puts("0"); return 0;}
    65         else {puts("1"); return 0;}
    66     }
    67     F(i,1,n){
    68         a[i]=getint()-1;
    69         if (a[i]<0 || a[i]>n-1) {puts("0"); return 0;}
    70         m+=a[i];
    71     }
    72     if (m!=n-2){puts("0"); return 0;}
    73     F(i,1,n) if (a[i]){
    74         C(m,a[i]);
    75         m-=a[i];
    76     }
    77     ans=1;
    78     F(i,1,tot) 
    79         F(j,1,b[i]) ans*=prime[i];
    80     printf("%lld
    ",ans);
    81     return 0;
    82 }
    View Code(组合数,处理阶乘时枚举每个数,进行分解质因数)
     1 /**************************************************************
     2     Problem: 1211
     3     User: ProgrammingApe
     4     Language: C++
     5     Result: Accepted
     6     Time:0 ms
     7     Memory:1288 kb
     8 ****************************************************************/
     9  
    10 //BZOJ 1211
    11 #include<cmath>
    12 #include<cstdio>
    13 #include<cstring>
    14 #include<cstdlib>
    15 #include<iostream>
    16 #include<algorithm>
    17 #define rep(i,n) for(int i=0;i<n;++i)
    18 #define F(i,j,n) for(int i=j;i<=n;++i)
    19 #define D(i,j,n) for(int i=j;i>=n;--i)
    20 #define pb push_back
    21 using namespace std;
    22 typedef long long LL;
    23 inline LL getint(){
    24     LL r=1,v=0; char ch=getchar();
    25     for(;!isdigit(ch);ch=getchar()) if (ch=='-') r=-1;
    26     for(; isdigit(ch);ch=getchar()) v=v*10-'0'+ch;
    27     return r*v;
    28 }
    29 const int N=401;
    30 /*******************template********************/
    31 LL n,m,a[N],ans,fac[N],prime[N],tot;
    32 LL b[N];
    33 bool vis[N];
    34 void ready(int n){
    35     F(i,2,n){
    36         if (!vis[i]) prime[++tot]=i;
    37         F(j,1,tot){
    38             if (i*prime[j]>n) break;
    39             vis[i*prime[j]]=1;
    40             if (i%prime[j]==0) break;
    41         }
    42     }
    43 }
    44 void add(int k,int v){
    45 //  printf("add %d %d
    ",k,v);
    46     F(j,1,tot){
    47         int x=k;
    48         while(x){
    49             b[j]+=x/prime[j]*v;
    50             x/=prime[j];
    51         }
    52     }
    53 }
    54 inline void C(int a,int b){ add(a,1); add(b,-1); add(a-b,-1);}
    55 int main(){
    56 #ifndef ONLINE_JUDGE
    57     freopen("tree.in","r",stdin);
    58     freopen("tree.out","w",stdout);
    59 #endif
    60     ready(300);
    61     n=getint();
    62     if (n==1){
    63         a[1]=getint();
    64         if (a[1]!=0) {puts("0"); return 0;}
    65         else {puts("1"); return 0;}
    66     }
    67     F(i,1,n){
    68         a[i]=getint()-1;
    69         if (a[i]<0 || a[i]>n-1) {puts("0"); return 0;}
    70         m+=a[i];
    71     }
    72     if (m!=n-2){puts("0"); return 0;}
    73     F(i,1,n) if (a[i]){
    74         C(m,a[i]);
    75         m-=a[i];
    76 //  F(j,1,4) cout <<prime[j]<<' '<<b[j]<<endl;
    77     }
    78 //  add(m,1);
    79 //  F(i,1,n) add(a[i],-1);
    80     ans=1;
    81 //  F(j,1,4) cout <<prime[j]<<' '<<b[j]<<endl;
    82     F(i,1,tot) 
    83         F(j,1,b[i]) ans*=prime[i];
    84     printf("%lld
    ",ans);
    85     return 0;
    86 }
    View Code(组合数,处理阶乘时直接处理n,分解质因数)

      嗯在看代码之前还有几句要说:这题有$nleq 150$,所以直接预处理阶乘是不太现实的……所以在这里我用了分解质因数进行快速乘除(这样做高精乘除快->变成质因子次数的加减,普通高精加减快)

      然后也学到了一种快速对阶乘进行因式分解的姿势>_<happy~

    1211: [HNOI2004]树的计数

    Time Limit: 10 Sec  Memory Limit: 162 MB
    Submit: 1458  Solved: 469
    [Submit][Status][Discuss]

    Description

    一个有n个结点的树,设它的结点分别为v1, v2, …, vn,已知第i个结点vi的度数为di,问满足这样的条件的不同的树有多少棵。给定n,d1, d2, …, dn,编程需要输出满足d(vi)=di的树的个数。

    Input

    第一行是一个正整数n,表示树有n个结点。第二行有n个数,第i个数表示di,即树的第i个结点的度数。其中1<=n<=150,输入数据保证满足条件的树不超过10^17个。

    Output

    输出满足条件的树有多少棵。

    Sample Input

    4
    2 1 2 1

    Sample Output

    2

    HINT

    Source

    [Submit][Status][Discuss]
  • 相关阅读:
    十大经典算法总结
    十大经典算法总结
    MySQL主从同步模拟
    MySQL主从同步模拟
    高斯定理
    高斯定理
    如何修改数据决策系统登陆地址为ip
    如何修改数据决策系统登陆地址为ip
    数据库连接池问题 Max Pool Size
    数据库连接池问题 Max Pool Size
  • 原文地址:https://www.cnblogs.com/Tunix/p/4506517.html
Copyright © 2020-2023  润新知