• 图论:Prufer编码


    BZOJ1211:使用prufer编码解决限定结点度数的树的计数问题

    首先学习一下prufer编码是干什么用的

    prufer编码可以与无根树形成一一对应的关系

    一种无根树就对应了一种prufer编码

    先介绍编码过程:

    选择无根树中度数为1的最小编号节点(也就是编号最小的叶子节点),将其删除,把它的邻接点加入数组

    不断执行上述操作直到树中仅剩两个节点

    解码过程:

    顺序扫描prufer编码数组,将扫到的第一个节点记为节点u,寻找不在prufer编码中的没有被标记的最小编号的节点v

    连接u-v并把v标记,将u从prufer编码数组删除并扫描下一个节点

    性质:

    一个点的入度为d,那么它最多有可能在prufer编码中出现d-1次

    prufer编码一共有n-2个数字,每个相同的数字出现d-1次

    针对这道题,如果我们给出了每个点的度数要求,那么满足要求的树的个数就是可生成的不同的prufer编码的个数:

    (n - 2) ! / ( (d1 - 1)! (d2 - 1)! ……(dn - 1)! ) 

    这样就是答案了

    下面介绍题目的实现方法(这个题比较简单,只是借助了prufer编码的性质进行计数,不涉及编码和解码的过程)

    const int maxn=155;
    int n,tot,cnt;
    int pri[maxn],d[maxn],num[maxn];
    long long ans=1;
    long long s[25];

    tot用来统计prufer编码中应有的节点数,看是否满足等于n-2

    cnt用来计数素数的个数,便于分解质因数

    pri里存的的是每一个质数,num里存的是质数出现的次数

    s用来预处理阶乘

    抛开这道题,我们重点应该学一学这个分解质因数的方法

    void solve(long long x,int f)  //按照指数分解质因数 
    {
        for(int i=1;i<=cnt;i++)
        {
            if(x<=1) return;
            while(x%pri[i]==0) {num[i]+=f;x/=pri[i];}
        }
    }

    满足题目的需求,直接计数即可:

        solve(s[n-2],1);  //计算阶乘并将结果分解质因数 
        for(int i=1;i<=n;i++) solve(s[d[i]],-1); //同上 
        for(int i=1;i<=cnt;i++)
            while(num[i]--) ans*=pri[i];  //统计结果 
        printf("%lld",ans);

    下面给完整的代码:

     1 #include<cmath>
     2 #include<cstdio>
     3 const int maxn=155;
     4 int n,tot,cnt;
     5 int pri[maxn],d[maxn],num[maxn];
     6 long long ans=1;
     7 long long s[25];
     8 inline int read()
     9 {
    10     int x=0,f=1;char ch=getchar();
    11     while(ch<'0'||ch>'9') {if(ch=='-')f=-1; ch=getchar();}
    12     while(ch>='0'&&ch<='9') {x*=10;x+=ch-'0';ch=getchar();}
    13     return x*f;
    14 }
    15 bool jud(int x)
    16 {
    17     for(int i=2;i<=sqrt(x);i++)
    18     if(x%i==0) return 0;
    19     return 1;
    20 }
    21 void getpri()
    22 {
    23     for(int i=2;i<=150;i++)
    24     if(jud(i)) pri[++cnt]=i;
    25 }
    26 void solve(long long x,int f)  //按照指数分解质因数 
    27 {
    28     for(int i=1;i<=cnt;i++)
    29     {
    30         if(x<=1) return;
    31         while(x%pri[i]==0) {num[i]+=f;x/=pri[i];}
    32     }
    33 }
    34 int main()
    35 {
    36     s[1]=1;
    37     for(int i=2;i<=22;i++) s[i]=s[i-1]*i;
    38     getpri();
    39     n=read();
    40     if(n==1)
    41     {
    42         int x=read();
    43         if(x==0) printf("1");
    44         else printf("0");
    45         return 0;
    46     }
    47     for(int i=1;i<=n;i++)
    48     {
    49         d[i]=read();
    50         if(d[i]==0) {printf("0");return 0;}
    51         d[i]--;
    52         tot+=d[i];
    53     }
    54     if(tot!=n-2) {printf("0"); return 0;}
    55     solve(s[n-2],1);  //计算阶乘并将结果分解质因数 
    56     for(int i=1;i<=n;i++) solve(s[d[i]],-1); //同上 
    57     for(int i=1;i<=cnt;i++)
    58         while(num[i]--) ans*=pri[i];  //统计结果 
    59     printf("%lld",ans);
    60     return 0;
    61 }
  • 相关阅读:
    使用Newtonsoft进行JSON序列化时将枚举序列化为字符串的方法
    通过javac导出Jar包
    JAVA中JDBC连接Mysql数据库简单测试
    使用Swashbuckle.AspNetCore生成.NetCore WEBAPI的接口文档
    CentOS7中使用yum安装Nginx的方法
    Dockerfile 指令 ADD 和 COPY介绍
    Azure Web应用中设置静态虚拟目录的方法(比如部署Django需要用到)
    pip常用命令(转载)
    android发布新版忘记keystore(jks)密码终极解决方案
    IntelliJ Idea 常用快捷键列表
  • 原文地址:https://www.cnblogs.com/aininot260/p/9424981.html
Copyright © 2020-2023  润新知