【题解】CJOI2019 登峰造鸡境 (Prufer序列+斯特林数)
【题解】CJOI2019 登峰造鸡境 (Prufer序列+斯特林数)
题目背景
舒服了。题目描述
你有一颗n个点的无根树,每个点有有一个标号(1~n)。
现在你知道,总共有m个叶子节点,求不同的树的形态方案数。
答案对\(10^9+7\)取模。下面是一些可能有用的定义:
叶子:度数为1的点。
不同:若对于两颗标号相同的树\(T1=(V,E_1),T2=(V,E_2)\),\(T1\neq T2\)当且仅当存在\((u,v) \in E_1 ,(u,v) \notin E_2\)输入格式
一共一行,第一行包含两个数n,m分别表示点的总个数和叶子数。
数据保证树一定存在。输出格式
一行一个整数,输出答案对\(10^9+7\)取模的结果。
输入样例1
5 3输出样例1
60子任务
对于\(10\%\)的数据,保证\(n,m<=5\)
对于\(20\%\)的数据,保证\(n,m<=10\)
对于\(40\%\)的数据,保证\(n,m<=20\)
对于\(60\%\)的数据,保证\(n,m<=5000\)
对于另外\(10\%\)的数据,保证\(m=2\)
对于另外\(10\%\)的数据,保证\(m=n-1\)
对于另外\(10\%\)的数据,保证\(m>=n-5\)
对于\(100\%\)的数据,保证\(n,m<=2\times 10^5\)
\(Solution\)
树的计数问题先通过一一对应转换为Prufer序列,再根据Prufer序列和第二类斯特林数求解。
Prufer序列
假设得到一颗有标号的树\(T\),我们通过这样的操作可以得到一个序列,这个序列和它对应的树是一一对应的。也就是说,任何两个不同的合法的Prufer序列都会对应出不同的两颗树。注意到这里的树是带编号的。
在树中,选取一个编号最小的叶子节点,将它的父亲节点加入Prufer序列,并且将这个叶子节点删去。
直到只剩下两个节点为止(只有一条边没有确定了),此时已经可以确定整个树的形态了。
那么得到了一个个数是\(n-2\)个的序列,这个序列和树的形态一一对应。那么这\(n-2\)个元素的序列可以构成
\[
n^{n-2}
\]
种组合。
根据一一对应法则,也就是说有n个不带标号节点的树总共有\(n^{n-2}\)种组合。
我们看一下这个序列的意义,一个节点在Prufer序列里出现的次数就是它的度数-1。那么现在问题就变成了,我要保证\(m\)个节点在Prufer序列里不出现。
第二类斯特林数
\(\begin{Bmatrix}n\\m\end{Bmatrix}\)表示\(n\)个元素划分为\(m\)个非空集合的方案数。
这里蕴藏的信息是:元素有区别,集合无区别。
递推公式
\[
{n \brace m}={n-1 \brace m-1}+m{n-1 \brace m}
\]
证明:见yyb博客。
容斥\(O(n)\)或者NTT\(O(n\log n)\)(求一列)
\[
S_2(n,m)=\begin{Bmatrix}n\\m\end{Bmatrix}=\frac 1 {m!} \sum_{i=0}^m (-1)^i{(m-i)^n}{m\choose i}
\]
证明:见yyb博客。
最后的答案
\[
(n-m)!\times{n\choose m}\times{n-2\brace n-m}
\]
因为斯特林数最后的盒子(集合)没有区别,然而我们这里是有区别的,所以应该乘上\((n-m)!\)补回来。
//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std; typedef long long ll;
inline int qr(){
register int ret=0,f=0;
register char c=getchar();
while(c<48||c>57)f|=c==45,c=getchar();
while(c>=48&&c<=57) ret=ret*10+c-48,c=getchar();
return f?-ret:ret;
}
const int mod=1e9+7;
const int maxn=2e5+5;
int jc[maxn];
int inv[maxn];
int n,m,ans,k;
typedef const int& ct;
inline int ksm(int base,ct p){
register int ret=1;
for(register int t=p;t;t>>=1,base=1ll*base*base%mod)
if(t&1) ret=1ll*ret*base%mod;
return ret;
}
int C(int n,int m){
if(n<m) return 0;
return 1ll*jc[n]*inv[m]%mod*inv[n-m]%mod;
}
int S(ct n,ct m){
register int ret=0;
for(register int t=0;t<=m;++t)
if(t&1) ret=(0ll+ret-1ll*ksm(m-t,n)*C(m,t)%mod+mod)%mod;
else ret=(0ll+ret+1ll*ksm(m-t,n)*C(m,t)%mod+mod)%mod;
return 1ll*ret*inv[m]%mod;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("dfzjj.in","r",stdin);
freopen("dfzjj.out","w",stdout);
#endif
jc[0]=1;
inv[0]=1;
for(register int t=1;t<maxn;++t) jc[t]=1ll*jc[t-1]*t%mod,inv[t]=1ll*inv[t-1]*ksm(t,mod-2LL)%mod;
n=qr();m=qr();
ans=1ll*jc[n-m]*C(n,m)%mod*S(n-2,n-m)%mod;
printf("%d\n",ans);
return 0;
}
【题解】CJOI2019 登峰造鸡境 (Prufer序列+斯特林数)的更多相关文章
- bzoj1211: [HNOI2004]树的计数(prufer序列+组合数学)
1211: [HNOI2004]树的计数 题目:传送门 题解: 今天刚学prufer序列,先打几道简单题 首先我们知道prufer序列和一颗无根树是一一对应的,那么对于任意一个节点,假设这个节点的度数 ...
- bzoj1211树的计数 x bzoj1005明明的烦恼 题解(Prufer序列)
1211: [HNOI2004]树的计数 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 3432 Solved: 1295[Submit][Stat ...
- 【XSY2519】神经元 prufer序列 DP
题目描述 有\(n\)点,每个点有度数限制,\(\forall i(1\leq i\leq n)\),让你选出\(i\)个点,再构造一棵生成树,要求每个点的度数不超过度数限制.问你有多少种方案. \( ...
- 【BZOJ1211】【HNOI2004】树的计数 prufer序列
题目描述 给你\(n\)和\(n\)个点的度数,问你有多少个满足度数要求的生成树. 无解输出\(0\).保证答案不超过\({10}^{17}\). \(n\leq 150\) 题解 考虑prufer序 ...
- 【XSY1295】calc n个点n条边无向连通图计数 prufer序列
题目大意 求\(n\)个点\(n\)条边的无向连通图的个数 \(n\leq 5000\) 题解 显然是一个环上有很多外向树. 首先有一个东西:\(n\)个点选\(k\)个点作为树的根的生成森林个数为: ...
- prufer序列学习笔记
prufer序列是一个定义在无根树上的东西. 构造方法是:每次选一个编号最小的叶子结点,把他的父亲的编号加入到序列的最后.然后删掉这个叶节点.直到最后只剩下两个节点,此时得到的序列就是prufer序列 ...
- 【BZOJ1005】[HNOI2008]明明的烦恼(prufer序列)
[BZOJ1005][HNOI2008]明明的烦恼(prufer序列) 题面 BZOJ 洛谷 题解 戳这里 #include<iostream> #include<cstdio> ...
- 【CF917D】Stranger Trees 树形DP+Prufer序列
[CF917D]Stranger Trees 题意:给你一棵n个点的树,对于k=1...n,问你有多少有标号的n个点的树,与给出的树有恰好k条边相同? $n\le 100$ 题解:我们先考虑容斥,求出 ...
- BZOJ4766: 文艺计算姬(Prufer序列)
题面 传送门 题解 结,结论题? 答案就是\(n^{m-1}m^{n-1}\) 我们考虑它的\(Prufer\)序列,最后剩下的两个点肯定是一个在左边一个在右边,设左边\(n\)个点,右边\(m\)个 ...
随机推荐
- bzoj 5123: [Lydsy1712月赛]线段树的匹配
设f[0/1][x]为区间[1,x]的根向下 不选(0)或者选(1) 的dp pair<最优值,方案数>. 可以很容易的发现总状态数就是log级别的,因为2*n 与 (2*n+1 或者 ...
- Scut游戏服务器引擎之新手入门
1. 开发语言:Scut提供C#或Python两种脚本语言开发,Python脚本的性能会比较差,建议使用编译执行的C#代码: 2. 运行平台:Scut可以Window与Linux平台上运行,Linux ...
- OpenCV头文件包含问题
opencv从2.2版本以后<opencv root>include下有两个文件夹 opencv 和opencv2.从官方的意思来看,它逐渐喜欢用opencv2里面的那种包含头文件的方式. ...
- 调用聚合数据新闻头条API
基于聚合数据新闻头条接口 支持阅读新闻类型包括: 各类社会.国内.国际.体育.娱乐.科技等资讯,更新周期5-30分钟. 新闻内容类型的多选,支持皮肤功能. 使用前需要有聚合数据账号,并实名制后通过 新 ...
- TCP/IP详解 卷一(第三章 IP:网际协议)
IP是TCP/IP协议族中最为核心的协议.所有的TCP.UDP.ICMP及IGMP数据都以IP数据报格式传输. IP提供不可靠.无连接的数据报传送服务. 1.不可靠:就是它不能保证IP数据报能成功地到 ...
- VueJS字符串反转:String.reverse()
HTML <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <titl ...
- ReactNative Navigator
https://facebook.github.io/react-native/docs/navigator.html Navigator实现了页面之间的跳转. Demo描述:打开即进入“课程”页面, ...
- XMLHttpRequest cannot load ''. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin ' ' is therefore not allowed access.
ajax跨域 禁止访问! 利用Access-Control-Allow-Origin响应头解决跨域请求
- 开源项目WebImageView载入图片
项目地址:https://github.com/ZaBlanc/WebImageView 作者对载入图片,以及图片的内存缓存和磁盘缓存做了封装. 代码量不多.可是可以满足一般的载入图片. 先看下项目结 ...
- erlang的RSA签名与验签
1.RSA介绍 RSA是目前最有影响力的公钥加密算法,该算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要对 其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥,即公钥,而 ...