Codeforces 917D - Stranger Trees(矩阵树定理/推式子+组合意义)
刚好看到 wjz 在做这题,心想这题之前好像省选前做过,当时觉得是道挺不错的题,为啥没写题解呢?于是就过来补了,由此可见我真是个大鸽子((
跑题了跑题了……
这里提供两种解法:
Algorithm 1.
注意到“恰好”二字有点蓝瘦,因此套路地想到二项式反演,也就说我们钦定 \(k\) 条边必须与原树中的边重合,其余边可以随便连的方案数,我们假设这些与原树中的边重合的边构成的集合为 \(E'\),那么 \(E'\) 中显然包含了一些连通块,而我们的任务就是在这些连通块之间连上一些边,使其成为一棵树,我们假设 \(E'\) 中的点组成了 \(r\) 个连通块,大小分别为 \(a_1,a_2,\cdots,a_r\),那么这里就有一个大家熟知,而我不知道的定理:构成的生成树个数为 \(n^{r-2}\prod\limits_{i=1}^ra_i\)。
证明:自己瞎 yy 的,证明错了不要打我(大雾
就是考虑 Prufer 序列,我们假设 \(i\) 在 Prufer 序列中出现的次数为 \(p_i\),那么可以列出柿子:
\[\begin{aligned}
|T|&=\sum\limits_{\sum p_i=r-2}\prod\limits_{i=1}^ra_i^{p_i+1}\times\dbinom{r-2}{p_1,p_2,\cdots,p_r}\\
&=\sum\limits_{\sum p_i=r-2}(r-2)!\prod\limits_{i=1}^r\dfrac{a_i^{p_i+1}}{p_i!}\\
&=\prod\limits_{i=1}^ra_i\times(r-2)!\sum\limits_{\sum p_i=r-2}\prod_{i=1}^r\dfrac{a_i^{p_i}}{p_i!}
\end{aligned}
\]注意到后面那坨东西就是 \(\prod\limits_{i=1}^r[x^{p_i}]e^{a_i}\),因此后面 \(\sum\) 里面的东西可以改写为 \(\sum\limits_{\sum p_i=n-2}\prod\limits_{i=1}^r[x^{p_i}]e^{a_i}=[x^{n-2}]\prod\limits_{i=1}^re^{a_i}=[x^{r-2}]e^n=n^{r-2}·\dfrac{1}{(r-2)!}\),故 \(|T|=\prod\limits_{i=1}^ra_i\times(r-2)!\times n^{r-2}\times(r-2)!=n^{r-2}\prod\limits_{i=1}^ra_i\)。
大功告成。
注意到当我们枚举重合边数 \(k\) 时,\(r=n-k\),因此前面 \(n^{r-2}\) 是常量,我们只需考虑后面的东西即可,而后面的东西有着清晰的组合意义:将原树分成 \(r\) 个连通块,每个连通块里恰好放一个球的方案数,因此考虑树形 \(dp\),\(dp_{u,j,0/1}\) 表示 \(u\) 的子树内划分为 \(j\) 个连通块,\(u\) 所在的连通块是否放了球的方案数。按照树上背包的套路合并即可,具体来说,如果我们要合并以 \(x,y\) 为根的子树,其中 \(x\) 为父亲,那么:
- \(dp_{x,i+j,0}\leftarrow dp_{x,i,0}\times dp_{y,j,1}\)
- \(dp_{x,i+j,1}\leftarrow dp_{x,i,1}\times dp_{y,j,1}\)
- \(dp_{x,i+j-1,0}\leftarrow dp_{x,i,0}\times dp_{y,j,0}\)
- \(dp_{x,i+j-1,1}\leftarrow dp_{x,i,0}\times dp_{y,j,1}+dp_{x,i,1}\times dp_{y,j,0}\)
时间复杂度 \(n^2\)。
const int MAXN=100;
const int MOD=1e9+7;
int qpow(int x,int e){
if(e<0) e+=MOD-1;int ret=1;
for(;e;e>>=1,x=1ll*x*x%MOD) if(e&1) ret=1ll*ret*x%MOD;
return ret;
}
int fac[MAXN+5],ifac[MAXN+5];
void init_fac(int n){
fac[0]=ifac[0]=ifac[1]=1;
for(int i=2;i<=n;i++) ifac[i]=1ll*(MOD-MOD/i)*ifac[MOD%i]%MOD;
for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%MOD,ifac[i]=1ll*ifac[i-1]*ifac[i]%MOD;
}
int binom(int x,int y){return 1ll*fac[x]*ifac[y]%MOD*ifac[x-y]%MOD;}
int n,hd[MAXN+5],to[MAXN*2+5],nxt[MAXN*2+5],ec=0;
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
int dp[MAXN+5][MAXN+5][2],siz[MAXN+5],tmp[MAXN+5][2];
int f[MAXN+5],g[MAXN+5];
void dfs(int x,int f){
dp[x][1][0]=dp[x][1][1]=siz[x]=1;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(y==f) continue;dfs(y,x);memset(tmp,0,sizeof(tmp));
for(int i=1;i<=siz[x];i++) for(int j=1;j<=siz[y];j++){
tmp[i+j][0]=(tmp[i+j][0]+1ll*dp[x][i][0]*dp[y][j][1])%MOD;
tmp[i+j][1]=(tmp[i+j][1]+1ll*dp[x][i][1]*dp[y][j][1])%MOD;
tmp[i+j-1][0]=(tmp[i+j-1][0]+1ll*dp[x][i][0]*dp[y][j][0])%MOD;
tmp[i+j-1][1]=(tmp[i+j-1][1]+1ll*dp[x][i][1]*dp[y][j][0]+1ll*dp[x][i][0]*dp[y][j][1])%MOD;
} siz[x]+=siz[y];
for(int i=1;i<=siz[x];i++) dp[x][i][0]=tmp[i][0],dp[x][i][1]=tmp[i][1];
}
// for(int i=1;i<=siz[x];i++) printf("%d %d %d %d\n",x,i,dp[x][i][0],dp[x][i][1]);
}
int main(){
scanf("%d",&n);
for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),adde(u,v),adde(v,u);
dfs(1,0);init_fac(n);
for(int i=1;i<=n;i++) f[n-i]=1ll*dp[1][i][1]*qpow(n,i-2)%MOD;
for(int i=0;i<n;i++) for(int j=i;j<n;j++){
if((j-i)&1) g[i]=(g[i]-1ll*f[j]*binom(j,i)%MOD+MOD)%MOD;
else g[i]=(g[i]+1ll*f[j]*binom(j,i))%MOD;
}
for(int i=0;i<n;i++) printf("%d%c",g[i],(i==n-1)?'\n':' ');
return 0;
}
Algorithm 2.
看到“生成树个数”,当然想到矩阵树定理咯(废话
但是本题一开始的树是没有权值的,因此我们要给这棵树赋上恰当的权值。注意到题目相当于从这 \(n\) 个点的完全图中找出一些生成树,对于生成树中的每条边,如果它也在原树中,那么会对重复边数产生 \(1\) 的贡献,否则对重复边数产生 \(0\) 的贡献。因此考虑建一张由这 \(n\) 个点组成的完全图,对于所有在原树中的边赋上权值 \(x\),其余的边赋上权值 \(1\),那么显然所有生成树权值乘积之和是一个多项式 \(P\),而 \(ans_k\) 就是 \([x^k]P\),这个很好理解。
不过如果我们暴力使用多项式进行计算,那复杂度将会高达 \(n^5\),不知道能不能过得去,因此考虑使用解多项式题的常用技巧——插值。根据 \(n+1\) 个点唯一确定一个 \(n\) 次多项式这个性质,我们考虑令 \(x=1,2,\cdots,n\) 分别跑一遍矩阵树定理,然后高斯消元解出系数即可。当然如果你闲着无聊写个拉格朗日插值或者更快的插值方法那我也不拦着你,瓶颈也不在从 \(n\) 个点值求出系数处/cy
时间复杂度 \(n^4\)
代码?抱歉,当时太菜了不会 Matrix-Tree 定理/wq,所以代码就咕着了(((
Codeforces 917D - Stranger Trees(矩阵树定理/推式子+组合意义)的更多相关文章
- [CF917D]Stranger Trees[矩阵树定理+解线性方程组]
题意 给你 \(n\) 个点的无向完全图,指定一棵树 \(S\),问有多少棵生成树和这棵树的公共边数量为 \(k\in[0,n-1]\) \(n\leq 100\) 分析 考虑矩阵树定理,把对应的树边 ...
- codeforces 917D Stranger Trees
题目链接 正解:矩阵树定理+拉格朗日插值. 一下午就搞了这一道题,看鬼畜英文题解看了好久.. 首先这题出题人给了两种做法,感觉容斥+$prufer$序列+$dp$的做法细节有点多所以没看,然而这个做法 ...
- LOJ 3399 -「2020-2021 集训队作业」Communication Network(推式子+组合意义+树形 DP)
题面传送门 一道推式子题. 首先列出柿子,\(ans=\sum\limits_{T_2}|T_1\cap T_2|·2^{T_1\cap T_2}\) 这个东西没法直接处理,不过注意到有一个柿子 \( ...
- sequence——强行推式子+组合意义
sequence 考虑长度<=x的方案数F(x),然后(F(x)-F(x-1))*x贡献到答案里 n平方的做法可以直接DP, 感觉有式子可言, 就推出式子:类似coat,每个长度为i的计算i次. ...
- CF917D. Stranger Trees & TopCoder13369. TreeDistance(变元矩阵树定理+高斯消元)
题目链接 CF917D:https://codeforces.com/problemset/problem/917/D TopCoder13369:https://community.topcoder ...
- LOJ #6044 -「雅礼集训 2017 Day8」共(矩阵树定理+手推行列式)
题面传送门 一道代码让你觉得它是道给初学者做的题,然鹅我竟没想到? 首先考虑做一步转化,我们考虑将整棵树按深度奇偶性转化为一张二分图,即将深度为奇数的点视作二分图的左部,深度为偶数的点视作二分图的右部 ...
- CodeForces - 156D:Clues(矩阵树定理&并查集)
题意:给定N点,M边,求添加最少的边使之变为连通图的方案数. 思路:注意题目给出的M边可能带环,即最后生成的不一定是一棵树.但是影响不大.根据矩阵树定理,我们知道生成树的数量=N^(N-2),即点数^ ...
- BZOJ 4766: 文艺计算姬 [矩阵树定理 快速乘]
传送门 题意: 给定一个一边点数为n,另一边点数为m,共有n*m条边的带标号完全二分图$K_{n,m}$ 求生成树个数 1 <= n,m,p <= 10^18 显然不能暴力上矩阵树定理 看 ...
- 【bzoj1002】[FJOI2007]轮状病毒 矩阵树定理+高精度
题目描述 轮状病毒有很多变种,所有轮状病毒的变种都是从一个轮状基产生的.一个N轮状基由圆环上N个不同的基原子和圆心处一个核原子构成的,2个原子之间的边表示这2个原子之间的信息通道.如下图所示 N轮状病 ...
随机推荐
- 《手把手教你》系列技巧篇(三十三)-java+ selenium自动化测试-单选和多选按钮操作-上篇(详解教程)
1.简介 在实际自动化测试过程中,我们同样也避免不了会遇到单选和多选的测试,特别是调查问卷或者是答题系统中会经常碰到.因此宏哥在这里直接分享和介绍一下,希望小伙伴或者童鞋们在以后工作中遇到可以有所帮助 ...
- 跟着老猫一起来学GO,环境搭建
老猫的GO学习系列博客已经正式发车了,相信大家以前学习一门编程语言的时候也有经验,咱们一般都是从环境开始,在此呢,大家也跟着老猫从最开始的搭建环境开始. GO语言的安装 首先呢,我们开始需要下载GO语 ...
- 单源最短路径算法:迪杰斯特拉 (Dijkstra) 算法(一)
一.算法介绍 迪杰斯特拉算法(英语:Dijkstra's algorithm)由荷兰计算机科学家艾兹赫尔·迪杰斯特拉在1956年提出.迪杰斯特拉算法使用了广度优先搜索解决赋权有向图的单源最短路径问题. ...
- 021中国大学生程序设计竞赛(CCPC)- 压力测试赛题解
A.Matrix 挺狗的一道题,从开始冲到最后都没冲出来,都没啥思路. 其实分开考虑每个数的贡献,这个想法也存在过,就是不知道该怎么计算,我们考虑我们单独考虑一个数字\(i(1\leq i\leq n ...
- hdu 1158 Employment Planning(DP)
题意: 有一个工程需要N个月才能完成.(n<=12) 给出雇佣一个工人的费用.每个工人每个月的工资.解雇一个工人的费用. 然后给出N个月所需的最少工人人数. 问完成这个项目最少需要花多少钱. 思 ...
- sqlldr 导入有中文乱码问题
1.导入成功后,查看导入数据有乱码 2.查看字符集为uft8 select * from v$nls_parameters where PARAMETER like '%NLS_CHARACTERSE ...
- 刷题日记-JZ25合并有序链表
合并有序链表 递归方式合并链表pHead1,pHead2 base case是 pHead1为空或者pHead2为空 递归方式是 如果pHead1->val < pHead2->va ...
- 讲分布式唯一id,这篇文章很实在
分布式唯一ID介绍 分布式系统全局唯一的 id 是所有系统都会遇到的场景,往往会被用在搜索,存储方面,用于作为唯一的标识或者排序,比如全局唯一的订单号,优惠券的券码等,如果出现两个相同的订单号,对于用 ...
- git push超过100M文件处理方法
git push超过100M文件处理方法 github 会在你上传文件大于50M的时候,给予警告 ; 大于100M的时候给出 server reject(拒绝上传) 解决方法 保持单个文件在 100 ...
- 记录线上APP一个排序比较引发的崩溃 Comparison method violates its general contract!
最近在做产品需求的时候上线了一个新的产品需求,给用户多了一种新的排序排序规则,更加方便用户找到自己想要的东西.新版本发布后,QA 给我发了一个 线上崩溃 bug 链接,具体内容如下: 看到上面的链接, ...