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 - ) ! / ( (d1 - )! (d2 - )! ……(dn - )! ) 

这样就是答案了

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

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

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

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

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

s用来预处理阶乘

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

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

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

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

下面给完整的代码:

 #include<cmath>
#include<cstdio>
const int maxn=;
int n,tot,cnt;
int pri[maxn],d[maxn],num[maxn];
long long ans=;
long long s[];
inline int read()
{
int x=,f=;char ch=getchar();
while(ch<''||ch>'') {if(ch=='-')f=-; ch=getchar();}
while(ch>=''&&ch<='') {x*=;x+=ch-'';ch=getchar();}
return x*f;
}
bool jud(int x)
{
for(int i=;i<=sqrt(x);i++)
if(x%i==) return ;
return ;
}
void getpri()
{
for(int i=;i<=;i++)
if(jud(i)) pri[++cnt]=i;
}
void solve(long long x,int f) //按照指数分解质因数
{
for(int i=;i<=cnt;i++)
{
if(x<=) return;
while(x%pri[i]==) {num[i]+=f;x/=pri[i];}
}
}
int main()
{
s[]=;
for(int i=;i<=;i++) s[i]=s[i-]*i;
getpri();
n=read();
if(n==)
{
int x=read();
if(x==) printf("");
else printf("");
return ;
}
for(int i=;i<=n;i++)
{
d[i]=read();
if(d[i]==) {printf("");return ;}
d[i]--;
tot+=d[i];
}
if(tot!=n-) {printf(""); return ;}
solve(s[n-],); //计算阶乘并将结果分解质因数
for(int i=;i<=n;i++) solve(s[d[i]],-); //同上
for(int i=;i<=cnt;i++)
while(num[i]--) ans*=pri[i]; //统计结果
printf("%lld",ans);
return ;
}

图论:Prufer编码的更多相关文章

  1. 图论:Prufer编码-Cayley定理

    BZOJ1430:运用Cayley定理解决树的形态统计问题 由Prufer编码可以引申出来一个定理:Cayley 内容是不同的n结点标号的树的数量为n^(n-2) 换一种说法就是一棵无根树,当知道结点 ...

  2. 树的prufer编码

    prufer是无根树的一种编码方式,一棵无根树和一个prufer编码唯一对应,也就是一棵树有唯一的prufer编码,而一个prufer编码对应一棵唯一的树. 第一部分:树编码成prufer序列. 树编 ...

  3. 【转】prufer编码

    既然有人提到了,就顺便学习一下吧,来源:http://greatkongxin.blog.163.com/blog/static/170097125201172483025666/ 一个含有n个点的完 ...

  4. [BZOJ1430] 小猴打架 (prufer编码)

    Description 一开始森林里面有N只互不相识的小猴子,它们经常打架,但打架的双方都必须不是好朋友.每次打完架后,打架的双方以及它们的好朋友就会互相认识,成为好朋友.经过N-1次打架之后,整个森 ...

  5. Codeforces 1109D. Sasha and Interesting Fact from Graph Theory 排列组合,Prufer编码

    原文链接https://www.cnblogs.com/zhouzhendong/p/CF1109D.html 题意 所有边权都是 [1,m] 中的整数的所有 n 个点的树中,点 a 到点 b 的距离 ...

  6. prufer编码

    看51nod的一场比赛,发现不会大家都A的一道题,有关prufer的 我去年4月就埋下prufer这个坑,一直没解决 prufer编码是什么 对于一棵无根树的生成的序列,prufer序列可以和无根树一 ...

  7. [bzoj1005][HNOI2008]明明的烦恼-Prufer编码+高精度

    Brief Description 给出标号为1到N的点,以及某些点最终的度数,允许在 任意两点间连线,可产生多少棵度数满足要求的树? Algorithm Design 结论题. 首先可以参考这篇文章 ...

  8. BZOJ 1430 小猴打架(prufer编码)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1430 [题目大意] 一开始森林里面有N只互不相识的小猴子,它们经常打架, 但打架的双方 ...

  9. 【Foreign】树 [prufer编码][DP]

    树 Time Limit: 10 Sec  Memory Limit: 256 MB Description Input Output Sample Input 3 2 2 1 Sample Outp ...

随机推荐

  1. HDU 1007 Quoit Design(计算几何の最近点对)

    Problem Description Have you ever played quoit in a playground? Quoit is a game in which flat rings ...

  2. C++ Mooc学习

    # C++远征篇之起航 1.IDE搭建,现在大部分同学都使用devC,devC的debug调试功能特别好用,可以跟踪变量.省去了在中间插入一些输出语句来输出中间变量的麻烦. 2.using names ...

  3. JAVA单态设计模式

    核心--在类的内部把构造器私有化,同时在内部产生对象,并通过类.静态方法(static)返回实例化对象的引用   设计模式是在大量的实践总结和理论化之后优选的代码结果,编程风格,以及解决问题的思考方式 ...

  4. Window Classes in Win32

    探索Win32系统之窗口类(Window Classes in Win32) Kyle MarshMicrosoft Developer Network Technology GroupMSDN技术组 ...

  5. VS2012或VS2010 工具栏中无法显示DevExpress控件

    进入命令提示符 跳转到Dev控件安装目录,如[目录D:\Program Files (x86)\DevExpress\DXperience 12.2\Tools]下, 然后执行命令: ToolboxC ...

  6. dev_queue_xmit 发生了什么?skb还会在哪里缓存

    见 codebox/net/qdisk/xmit.log中保存了一份记录 调用关系 sch_direct_xmit --> dev_hard_start_xmit --> xmit_one ...

  7. BZOJ4864 BeiJing 2017 Wc神秘物质(splay)

    splay维护区间最大值.最小值.相邻两数差的绝对值的最小值即可. #include<iostream> #include<cstdio> #include<cmath& ...

  8. CMD命令提示符

    mspaint  画图板 notepad  打开记事本 write  写字板 calc.exe  计算器 control.exe  控制面板 osk  打开屏幕键盘 rononce -p ----15 ...

  9. CentOS yum安装软件包

    yum(Yellowdog Update Modifie)命令是在Fedora和RedHat以及SUSE中基于rpm的软件包管理器,它可以使系统管理人员交互和自动化地更细与管理RPM软件包,能够从指定 ...

  10. IntelliJ IDEA2018注册

    第一步:0.0.0.0 account.jetbrains.com及0.0.0.0 www.jetbrains.com  添加到hosts文件 第二步:进入 http://idea.lanyus.co ...