传送门

好神仙的题目……又一次有了做一题学一堆的美好体验

据说本题有第二类斯特林数+分治\(FFT\)的做法,然而咱实在看不懂写的是啥,题解贴这里,有兴趣的可以自己去瞅瞅,看懂了记得回来跟咱讲讲

前置芝士

\(prufer\)序列

\(prufer\)序列是个啥?

对于一棵无根树,我们找到它的标号最小的叶子,删去它,并记下与它相邻的节点的标号。重复这个过程直到树上的节点数为\(2\)为止。这个时候我们得到了一个长度为\(n-2\)的序列就是这棵无根树的\(prufer\)序列

很明显,每一棵无根树唯一对应一个\(prufer\)序列,我们只要能证明每一个\(prufer\)序列都唯一对应一棵无根树,这两者之间就能有一个一一对应的关系了

考虑一个\(prufer\)序列,对于图中某个节点\(u\),如果它在原图中不是叶子,那么与它相邻的边至少有两条。可操作完之后整个图中的边只剩下了一条,所以每一个不是叶子的节点都会在\(prufer\)序列中出现

我们把没有出现在序列中的数字排序,那么最小的数字肯定是和序列中的第一个数字配对,那么原图中它们之间肯定连边

然后我们递归考虑序列的后面几位,不难发现每一次的连边情况都唯一。于是我们知道每一个\(prufer\)序列唯一的对应一棵无根树

综上,无根树和\(prufer\)序列有着一一对应的关系

从中我们也可以看出,对于一个无向完全图的生成树,它的\(prufer\)序列有\(n-2\)个值,每个值的取值范围是\([1,n]\),所以一个无向完全图的生成树个数是\(n^{n-2}\)

快速求数列前\(k\)次方和

咱会差值

咱会第二类斯特林数

然而我们现在需要的是对于任意\(0\leq j\leq k\),求出\(\sum_{i=1}^n {a_i}^j\)

咱刚刚啥都没说您继续

考虑答案的生成函数$$F(x)=\sum_{j=0}k\sum_{i=1}n{a_i}jxj=\sum_{i=1}n\sum_{j=0}k(a_ix)j=\sum_{i=1}n\frac{1}{1-a_ix}$$

因为有$$\ln'(\frac{1}{1-a_ix})=\frac{-a_i}{1-a_ix}=\sum_{j=0}^\infty (a_ix)^j\times (-a_i)$$

那么我们可以先计算出\(G(x)=\sum_{i=1}^n\sum_{j=0}^k (a_ix)^j\times (-a_i)\),则\(F(x)=-x\times G(x)+n\)

而\(G(x)\)就吼算啦

\[G(x)=\sum_{i=1}^n \ln'(\frac{1}{1-a_ix})=(\sum_{i=1}^n \ln\frac{1}{1-a_ix})'=\ln'(\prod_{i=1}^n \frac{1}{1-a_ix})
\]

括号里的可以用分治\(FFT\)计算了

本题题解

首先对于每一棵生成树\(T\),它的贡献为\(\prod_{i=1}^n{a_i}^{d_i}{d_i}^m\sum_{i=1}^n{d_i}^m\)

那么考虑枚举每一个\(prufer\)序列来统计总贡献

\[Ans=(n-2)!\sum_{\sum d_i=n-2}\prod_{i=1}^n \frac{{a_i}^{d_i+1}}{d_i!}(d_i+1)^m\sum_{i=1}^m(d_i+1)^m
\]

\[Ans=(n-2)!\prod_{i=1}^na_i\sum_{\sum d_i=n-2}\prod_{i=1}^n \frac{{a_i}^{d_i}}{d_i!}(d_i+1)^m\sum_{i=1}^m(d_i+1)^m
\]

前面的\((n-2)!\prod_{i=1}^na_i\)是常量,不用去管,考虑后面的\(\prod_{i=1}^n \frac{{a_i}^{d_i}}{d_i!}(d_i+1)^m\sum_{i=1}^m(d_i+1)^m\),它等价于

\[\prod_{i=1}^n \frac{{a_i}^{d_i}}{d_i!}(d_i+1)^{2m}\sum_{j=1,j\neq i}^m\frac{{a_j}^{d_j}}{d_j!}(d_j+1)^{m}
\]

因为需要\(\sum d_i=n-2\),我们构造关于\(d\)的生成函数

\[A(x)=\sum_{i}\frac{x^i(i+1)^{2m}}{i!}
\]

\[B(x)=\sum_{i}\frac{x^i(i+1)^{m}}{i!}
\]

那么原式就等于$$F(x)=\sum_{i=1}^n A(a_ix)\prod_{j=1,j\neq i}^nB(a_jx)$$

\[F(x)=\sum_{i=1}^n \frac{A(a_ix)}{B(a_ix)}\prod_{j=1}^nB(a_jx)
\]

\[F(x)=\sum\frac{A(a_ix)}{B(a_ix)}\exp(\sum\ln B(a_jx))
\]

我们求出\(\frac{A(x)}{B(x)}\)和\(\ln B(x)\)之后,要把\(a_ix\)代入并求和,那么第\(k\)项的系数要乘上\(\sum_{i=1}^n{a_i}^k\),这个就是前面说的可以快速求的东西

于是复杂度就为\(O(n\log^2 n)\)

//minamoto
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(R int i=a,I=b-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
R int res,f=1;R char ch;
while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
return res*f;
}
const int N=1e5+5,P=998244353,Gi=332748118;
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
R int res=1;
for(;y;y>>=1,x=mul(x,x))if(y&1)res=mul(res,x);
return res;
}
int E[N],B[N],F[N],C[N],D[N],O[N],r[N],G[N];
void NTT(int *A,int ty,int len){
int lim=1,l=0;while(lim<len)lim<<=1,++l;
fp(i,0,lim-1)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
fp(i,0,lim-1)if(i<r[i])swap(A[i],A[r[i]]);
for(R int mid=1;mid<lim;mid<<=1){
int I=(mid<<1),Wn=ksm(ty==1?3:Gi,(P-1)/I);O[0]=1;
fp(i,1,mid-1)O[i]=mul(O[i-1],Wn);
for(R int j=0;j<lim;j+=I)fp(k,0,mid-1){
int x=A[j+k],y=mul(O[k],A[j+k+mid]);
A[j+k]=add(x,y),A[j+k+mid]=dec(x,y);
}
}
if(ty==-1)for(R int i=0,inv=ksm(lim,P-2);i<lim;++i)A[i]=mul(A[i],inv);
}
void Inv(int *a,int *b,int len){
if(len==1)return b[0]=ksm(a[0],P-2),void();
Inv(a,b,len>>1);fp(i,0,len-1)C[i]=a[i],D[i]=b[i];
NTT(C,1,len<<1),NTT(D,1,len<<1);
fp(i,0,(len<<1)-1)C[i]=mul(mul(C[i],D[i]),D[i]);
NTT(C,-1,len<<1);
fp(i,0,len-1)b[i]=dec(add(b[i],b[i]),C[i]);
fp(i,0,(len<<1)-1)C[i]=D[i]=0;
}
void Direv(int *A,int *B,int len){
fp(i,1,len-1)B[i-1]=mul(A[i],i);B[len-1]=0;
}
void Inter(int *A,int *B,int len){
fp(i,1,len-1)B[i]=mul(A[i-1],ksm(i,P-2));B[0]=0;
}
void Ln(int *a,int *b,int len){
Inv(a,E,len),Direv(a,F,len);
NTT(E,1,len<<1),NTT(F,1,len<<1);
fp(i,0,(len<<1)-1)E[i]=mul(E[i],F[i]);
NTT(E,-1,len<<1),Inter(E,b,len);
fp(i,0,(len<<1)-1)E[i]=F[i]=0;
}
void Exp(int *a,int *b,int len){
if(len==1)return b[0]=1,void();
Exp(a,b,len>>1),Ln(b,B,len);
B[0]=dec(a[0]+1,B[0]);fp(i,1,len-1)B[i]=dec(a[i],B[i]);
NTT(B,1,len<<1),NTT(b,1,len<<1);
fp(i,0,(len<<1)-1)b[i]=mul(b[i],B[i]);
NTT(b,-1,len<<1);fp(i,len,(len<<1)-1)b[i]=B[i]=0;
}
int sz[N],A[19][N],TA[N],TB[N],TC[N],sum[N],ta[N],tb[N],tc[N];
void solve(int ql,int qr,int d){
if(ql==qr)return A[d][0]=1,A[d][1]=P-sz[ql],void();
int mid=(ql+qr)>>1;
solve(ql,mid,d),solve(mid+1,qr,d+1);
int lim=1;while(lim<=qr-ql+1)lim<<=1;
fp(i,mid-ql+2,lim-1)A[d][i]=0;
fp(i,qr-mid+1,lim-1)A[d+1][i]=0;
NTT(A[d],1,lim),NTT(A[d+1],1,lim);
fp(i,0,lim-1)A[d][i]=mul(A[d][i],A[d+1][i]);
NTT(A[d],-1,lim);
}
int n,m,res,fac[N],inv[N];
int main(){
// freopen("testdata.in","r",stdin);
n=read(),m=read();if(n==1)return puts("1"),0;
fac[0]=inv[0]=1;fp(i,1,n)fac[i]=mul(fac[i-1],i);
inv[n]=ksm(fac[n],P-2);fd(i,n-1,1)inv[i]=mul(inv[i+1],i+1);
fp(i,1,n)sz[i]=read();
solve(1,n,0);
fp(i,0,n)tc[i]=A[0][i];
int len=1;while(len<=n)len<<=1;
Ln(tc,sum,len);
fp(i,1,n)sum[i]=P-mul(sum[i],i);
sum[0]=n;
fp(i,0,n-1)TA[i]=mul(ksm(i+1,m),inv[i]),TB[i]=mul(ksm(i+1,m<<1),inv[i]);
Ln(TA,tc,len),Inv(TA,TC,len);
NTT(TC,1,len<<1),NTT(TB,1,len<<1);
fp(i,0,(len<<1)-1)TB[i]=mul(TB[i],TC[i]);
NTT(TB,-1,len<<1);
memset(TA,0,sizeof(TA));
memset(TC,0,sizeof(TC));
fp(i,0,n-1)TB[i]=mul(TB[i],sum[i]),TA[i]=mul(tc[i],sum[i]);
Exp(TA,TC,len);
// fp(i,0,(len<<1)-1)printf("%d %d\n",i,TC[i]);
fp(i,n,(len<<1)-1)TB[i]=0;
NTT(TB,1,len<<1),NTT(TC,1,len<<1);
fp(i,0,(len<<1)-1)TB[i]=mul(TB[i],TC[i]);
NTT(TB,-1,len<<1);
res=mul(TB[n-2],fac[n-2]);
fp(i,1,n)res=mul(res,sz[i]);
printf("%d\n",res);
return 0;
}

uoj#335. 【清华集训2017】生成树计数(prufer序列+生成函数+多项式)的更多相关文章

  1. 洛谷 P4002 - [清华集训2017]生成树计数(多项式)

    题面传送门 神题. 考虑将所有连通块缩成一个点,那么所有连好边的生成树在缩点之后一定是一个 \(n\) 个点的生成树.我们记 \(d_i\) 为第 \(i\) 个连通块缩完点之后的度数 \(-1\), ...

  2. Loj 2320.「清华集训 2017」生成树计数

    Loj 2320.「清华集训 2017」生成树计数 题目描述 在一个 \(s\) 个点的图中,存在 \(s-n\) 条边,使图中形成了 \(n\) 个连通块,第 \(i\) 个连通块中有 \(a_i\ ...

  3. 【UOJ#340】【清华集训2017】小 Y 和恐怖的奴隶主(矩阵快速幂,动态规划)

    [UOJ#340][清华集训2017]小 Y 和恐怖的奴隶主(矩阵快速幂,动态规划) 题面 UOJ 洛谷 题解 考虑如何暴力\(dp\). 设\(f[i][a][b][c]\)表示当前到了第\(i\) ...

  4. [UOJ#274][清华集训2016]温暖会指引我们前行

    [UOJ#274][清华集训2016]温暖会指引我们前行 试题描述 寒冬又一次肆虐了北国大地 无情的北风穿透了人们御寒的衣物 可怜虫们在冬夜中发出无助的哀嚎 “冻死宝宝了!” 这时 远处的天边出现了一 ...

  5. Loj #2331. 「清华集训 2017」某位歌姬的故事

    Loj #2331. 「清华集训 2017」某位歌姬的故事 IA 是一名会唱歌的女孩子. IOI2018 就要来了,IA 决定给参赛选手们写一首歌,以表达美好的祝愿.这首歌一共有 \(n\) 个音符, ...

  6. Loj #2324. 「清华集训 2017」小 Y 和二叉树

    Loj #2324. 「清华集训 2017」小 Y 和二叉树 小Y是一个心灵手巧的OIer,她有许多二叉树模型. 小Y的二叉树模型中,每个结点都具有一个编号,小Y把她最喜欢的一个二叉树模型挂在了墙上, ...

  7. Loj #2321. 「清华集训 2017」无限之环

    Loj #2321. 「清华集训 2017」无限之环 曾经有一款流行的游戏,叫做 *Infinity Loop***,先来简单的介绍一下这个游戏: 游戏在一个 \(n \times m\) 的网格状棋 ...

  8. Luogu P5296 [北京省选集训2019]生成树计数

    Luogu P5296 [北京省选集训2019]生成树计数 题目链接 题目大意:给定每条边的边权.一颗生成树的权值为边权和的\(k\)次方.求出所有生成树的权值和. 我们列出答案的式子: 设\(E\) ...

  9. loj #2325. 「清华集训 2017」小Y和恐怖的奴隶主

    #2325. 「清华集训 2017」小Y和恐怖的奴隶主 内存限制:256 MiB时间限制:2000 ms标准输入输出 题目类型:传统评测方式:文本比较   题目描述 "A fight? Co ...

随机推荐

  1. <raspberry pi > 用树莓派来听落网电台

    树莓派放在抽屉里吃灰有半年多了,去年玩了1个月后就没怎么开整了,上个月没工作,刚好有点闲暇,就把树莓派翻出来折腾,刚好碰到落网改版了,想起以前在树莓派论坛看到有网友拿树莓派来听豆瓣电台,代码那时我都下 ...

  2. testng ITestListener使用

    ITestListener适用场景 当使用testng执行测试时,我们常会想在某个阶段做一些特别的处理,比如:测试成功结束后,测试失败后,跳过某个脚本后,全部脚本执行完毕后.要想达成这个目标,我们需要 ...

  3. linux版本查看命令

    uname -a显示电脑以及操作系统相关信息 cat /proc/version显示正在运行的内核版本 cat /etc/issue或cat /etc/redhat-release  Linux查看版 ...

  4. drawable canvas使用

    /** * Drawable 就是一个可画的对象, * 其可能是一张位图(BitmapDrawable), * 也可能是一个图形(ShapeDrawable), * 还有可能是一个图层(LayerDr ...

  5. 不使用 spring-boot-starter-parent 构建 spring boot 应用

    创建 spring-boot 应用通用方法是配置 pom.xml,定义 为 spring-boot-start-parent.如下: <parent> <groupId>org ...

  6. ES6 Fetch API HTTP请求实用指南

    本次将介绍如何使用Fetch API(ES6 +)对REST API的 HTTP请求,还有一些示例提供给大家便于大家理解. 注意:所有示例均在带有箭头功能的 ES6中给出. 当前的Web /移动应用程 ...

  7. openfire build

    1. build path: a) source folder:包括openfire和各插件的代码. b) libraries:build/lib下jar包和插件下jar包,jdk/lib/tools ...

  8. Linux_学习_01_常用命令大全

    一.文件目录 1.列出目录 ls -a #显示隐藏文件 -l #显示文件和目录的详细资料 -h -lrt tree #显示文件和目录由根目录开始的树形结构 lstree #显示文件和目录由根目录开始的 ...

  9. 2018.3.3 How too much fructose may cause liver damage

    Fructose is the sweetest of the natural sugars. As its name suggests, it is found mainly in fruits. ...

  10. kmp算法模板及理解

    kmp算法是复杂度为O(n+m)的字符串匹配算法; 首先kmp算法的核心是在模式串中获得next数组,这个数组表示模式串的子串的前缀和后缀相同的最长长度; 这样在匹配的过程中如果指到不匹配的位置,模式 ...