Loj 2320.「清华集训 2017」生成树计数
Loj 2320.「清华集训 2017」生成树计数
题目描述
在一个 \(s\) 个点的图中,存在 \(s-n\) 条边,使图中形成了 \(n\) 个连通块,第 \(i\) 个连通块中有 \(a_i\) 个点。
现在我们需要再连接 \(n-1\) 条边,使该图变成一棵树。对一种连边方案,设原图中第 \(i\) 个连通块连出了 \(d_i\) 条边,那么这棵树 \(T\) 的价值为:
\]
你的任务是求出所有可能的生成树的价值之和,对 \(998244353\) 取模。
输入格式
输入的第一行包含两个整数 \(n,m\),意义见题目描述。
接下来一行有 \(n\) 个整数,第 \(i\) 个整数表示 \(a_i\) \((1\le a_i< 998244353)\)。
* 你可以由 \(a_i\) 计算出图的总点数 \(s\),所以在输入中不再给出 \(s\) 的值。
输出格式
输出包含一行一个整数,表示答案。
数据范围与提示
本题共有 \(20\) 个测试点,每个测试点 \(5\) 分。
- \(20\%\) 的数据中,\(n\le500\)。
- 另外 \(20\%\) 的数据中,\(n \le 3000\)。
- 另外 \(10\%\) 的数据中,\(n \le 10010, m = 1\)。
- 另外 \(10\%\)的数据中,\(n \le 10015,m = 2\)。
-另外 \(20\%\) 的数据中,所有 \(a_i\) 相等。
\(\\\)
好神的题啊!
假设我们知道了每个点的度数,考虑计算此时的生成树的个数。这个用\(prufer\)序列非常好解决:
假设第\(i\)个点在\(prufer\)序列中出现次数为\(d_i\),(则其度数为\(d_i+1\))
\]
先考虑对式子进行变形
Ans&=
\sum_{\sum d_i==n-2}
(n-2)!
\sum_{i=1}^n\frac{{{a_i}^{d_i+1}d_i}^{2m}}{d_i!}
\prod_{j=1,j\neq i}^n\frac{{d_j}^m}{d_j!}\\
&=(n-2)!\prod_{i=1}^na_i
\sum_{\sum_{d_i==n-2}}\sum_{i=1}^n\frac{{{a_i}^{d_i}d_i}^{2m}}{d_i!}
\prod_{j=1,j\neq i}^n\frac{{d_j}^m}{d_j!}\\
\end{align}
\]
设
\]
考虑用生成函数解决:
B(x)=\sum_{i=0}^n\frac{i^m}{i!}x^i
\]
则\(Ans'\)的生成函数为
=\sum_{i=1}^n\frac{A(a_i)}{B(a_i)}\prod_{j=1}^nB(a_j)
\]
对于\(\prod_{j=1}^nB(a_j)\),我们的一般套路是将其写成
=\exp(\sum_{j=1}^n\ln(B(a_j)))
\]
这样做的好处是我们只需要求出\(\ln(B(x))\),然后对第\(i\)项系数乘上\(\displaystyle \sum_{j=1}^n{a_j}^i\)就可以得到\(\displaystyle \sum_{j=1}^n\ln(B(a_j))\)了。对于\(\displaystyle \sum_{i=1}^n\frac{A(a_i)}{B(a_i)}\)我们也 用相同的处理方式。
所以:
\]
现在的问题是如何求出
\]
考虑\(\ln(x)\)的取\(x_0=1\)时的泰勒展开形式
=\sum_{i=1}\frac{(-1)^{i-1}}{i}(x-1)^i
\]
所以:
\]
那么我们只需要求出
\]
就行了。
\]
\(\prod_{i=1}^n(1+a_ix)\)可以用分治\(NTT\)求出。
代码:
#include<bits/stdc++.h>
#define ll long long
#define N 200005
using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
const ll mod=998244353;
ll ksm(ll t,ll x) {
ll ans=1;
for(;x;x>>=1,t=t*t%mod)
if(x&1) ans=ans*t%mod;
return ans;
}
int n,m;
int a[N];
void NTT(ll *a,int d,int flag) {
int n=1<<d;
static int rev[N<<2];
static ll G=3;
for(int i=0;i<n;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<d-1);
for(int i=0;i<n;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
for(int s=1;s<=d;s++) {
int len=1<<s,mid=len>>1;
ll w=flag==1?ksm(G,(mod-1)/len):ksm(G,mod-1-(mod-1)/len);
for(int i=0;i<n;i+=len) {
ll t=1;
for(int j=0;j<mid;j++,t=t*w%mod) {
ll u=a[i+j],v=a[i+j+mid]*t%mod;
a[i+j]=(u+v)%mod;
a[i+j+mid]=(u-v+mod)%mod;
}
}
}
if(flag==-1) {
ll inv=ksm(n,mod-2);
for(int i=0;i<n;i++) a[i]=a[i]*inv%mod;
}
}
ll A[N<<2],B[N<<2];
ll inv[N<<2];
ll f[N<<2],g[N<<2];
void Inv(ll *inv,ll *a,int d) {
static ll A[N<<3];
if(d==0) {
inv[0]=ksm(a[0],mod-2);
return ;
}
Inv(inv,a,d-1);
for(int i=0;i<1<<d;i++) A[i]=a[i];
for(int i=1<<d;i<1<<d+1;i++) inv[i]=A[i]=0;
NTT(A,d+1,1);
NTT(inv,d+1,1);
for(int i=0;i<1<<d+1;i++) {
inv[i]=(2*inv[i]-A[i]*inv[i]%mod*inv[i]%mod+mod)%mod;
}
NTT(inv,d+1,-1);
for(int i=1<<d;i<1<<d+1;i++) inv[i]=0;
}
void Der(ll *a,int d) {
int n=1<<d;
for(int i=0;i<n-1;i++) a[i]=(i+1)*a[i+1]%mod;
a[n-1]=0;
}
void Int(ll *a,int d) {
int n=1<<d;
for(int i=n-1;i>0;i--) a[i]=ksm(i,mod-2)*a[i-1]%mod;
a[0]=0;
}
ll ln[N<<2];
void Ln(ll *ln,ll *a,int d) {
static ll der[N<<2];
for(int i=0;i<1<<d+1;i++) der[i]=0;
for(int i=0;i<1<<d;i++) der[i]=a[i];
Inv(inv,a,d);
Der(der,d);
NTT(inv,d+1,1),NTT(der,d+1,1);
for(int i=0;i<1<<d+1;i++) ln[i]=inv[i]*der[i]%mod;
NTT(ln,d+1,-1);
for(int i=1<<d;i<1<<d+1;i++) ln[i]=0;
Int(ln,d);
for(int i=1<<d;i<1<<d+1;i++) ln[i]=0;
}
ll ex[N<<2];
void Exp(ll *exp,ll *a,int d) {
static ll A[N<<2],B[N<<2];
if(d==0) {
exp[0]=1;
return ;
}
Exp(exp,a,d-1);
for(int i=0;i<1<<d;i++) A[i]=a[i];
for(int i=1<<d;i<1<<d+1;i++) exp[i]=A[i]=0;
Ln(B,exp,d);
NTT(exp,d+1,1);
NTT(B,d+1,1);
NTT(A,d+1,1);
for(int i=0;i<1<<d+1;i++) {
exp[i]=exp[i]*(1-B[i]+A[i]+mod)%mod;
}
NTT(exp,d+1,-1);
for(int i=1<<d;i<1<<d+1;i++) exp[i]=0;
}
void solve(int l,int r,ll *a) {
static ll A[N<<2],B[N<<2];
if(l==r) return ;
int mid=l+r>>1;
solve(l,mid,a),solve(mid+1,r,a);
int d=ceil(log2(r-l+2));
for(int i=0;i<1<<d;i++) A[i]=B[i]=0;
for(int i=l;i<=mid;i++) A[i-l+1]=a[i];
for(int i=mid+1;i<=r;i++) B[i-mid]=a[i];
A[0]=B[0]=1;
NTT(A,d,1),NTT(B,d,1);
for(int i=0;i<1<<d;i++) A[i]=A[i]*B[i]%mod;
NTT(A,d,-1);
for(int i=l;i<=r;i++) a[i]=A[i-l+1];
}
ll summ[N];
ll cal(int k) {
ll ans=0;
for(int i=1;i<=n;i++) (ans+=ksm(a[i],k))%=mod;
return ans;
}
ll tem[N<<2];
ll fac[N],ifac[N];
int main() {
n=Get(),m=Get();
fac[0]=1;
for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
ifac[n]=ksm(fac[n],mod-2);
for(int i=n-1;i>=0;i--) ifac[i]=ifac[i+1]*(i+1)%mod;
for(int i=1;i<=n;i++) a[i]=Get();
for(int i=1;i<=n;i++) summ[i]=a[i];
int d=ceil(log2(2*n+1));
solve(1,n,summ);
summ[0]=1;
Ln(ln,summ,d);
memcpy(summ,ln,sizeof(summ));
summ[0]=n;
for(int i=1;i<=n;i++) {
if(!(i&1)) summ[i]=summ[i]*(mod-1)%mod;
summ[i]=summ[i]*i%mod;
}
for(int i=0;i<=n;i++) {
A[i]=ksm(i+1,2*m)*ifac[i]%mod;
B[i]=ksm(i+1,m)*ifac[i]%mod;
}
Ln(ln,B,d);
for(int i=0;i<1<<d;i++) ln[i]=ln[i]*summ[i]%mod;
for(int i=n;i<1<<d;i++) ln[i]=0;
Exp(g,ln,d);
for(int i=n;i<=1<<d;i++) g[i]=0;
Inv(inv,B,d);
for(int i=n;i<1<<d;i++) inv[i]=0;
NTT(inv,d,1),NTT(A,d,1);
for(int i=0;i<1<<d;i++) f[i]=inv[i]*A[i]%mod;
NTT(f,d,-1);
for(int i=0;i<1<<d;i++) f[i]=f[i]*summ[i]%mod;
for(int i=n;i<1<<d;i++) f[i]=0;
NTT(f,d,1),NTT(g,d,1);
for(int i=0;i<1<<d;i++) f[i]=f[i]*g[i]%mod;
NTT(f,d,-1);
ll ans=fac[n-2];
for(int i=1;i<=n;i++) ans=ans*a[i]%mod;
ans=ans*f[n-2]%mod;
cout<<ans;
return 0;
}
Loj 2320.「清华集训 2017」生成树计数的更多相关文章
- 【LOJ】#2320. 「清华集训 2017」生成树计数
题解 我,理解题解,用了一天 我,卡常数,又用了一天 到了最后,我才发现,我有个加法取模,写的是while(c >= MOD) c -= MOD 我把while改成if,时间,少了 六倍. 六倍 ...
- LOJ2320「清华集训 2017」生成树计数
由于菜鸡的我实在是没学会上升幂下降幂那一套理论,这里用的是完全普通多项式的做法. 要是有大佬愿意给我讲讲上升幂下降幂那一套东西,不胜感激orz! 首先可以想到prufer序列,如果不会的话可以左转百度 ...
- Loj #2331. 「清华集训 2017」某位歌姬的故事
Loj #2331. 「清华集训 2017」某位歌姬的故事 IA 是一名会唱歌的女孩子. IOI2018 就要来了,IA 决定给参赛选手们写一首歌,以表达美好的祝愿.这首歌一共有 \(n\) 个音符, ...
- Loj #2324. 「清华集训 2017」小 Y 和二叉树
Loj #2324. 「清华集训 2017」小 Y 和二叉树 小Y是一个心灵手巧的OIer,她有许多二叉树模型. 小Y的二叉树模型中,每个结点都具有一个编号,小Y把她最喜欢的一个二叉树模型挂在了墙上, ...
- Loj #2321. 「清华集训 2017」无限之环
Loj #2321. 「清华集训 2017」无限之环 曾经有一款流行的游戏,叫做 *Infinity Loop***,先来简单的介绍一下这个游戏: 游戏在一个 \(n \times m\) 的网格状棋 ...
- [LOJ#2330]「清华集训 2017」榕树之心
[LOJ#2330]「清华集训 2017」榕树之心 试题描述 深秋.冷风吹散了最后一丝夏日的暑气,也吹落了榕树脚下灌木丛的叶子.相识数年的Evan和Lyra再次回到了小时候见面的茂盛榕树之下.小溪依旧 ...
- [LOJ#2329]「清华集训 2017」我的生命已如风中残烛
[LOJ#2329]「清华集训 2017」我的生命已如风中残烛 试题描述 九条可怜是一个贪玩的女孩子. 这天她在一堵墙钉了 \(n\) 个钉子,第 \(i\) 个钉子的坐标是 \((x_i,y_i)\ ...
- [LOJ#2328]「清华集训 2017」避难所
[LOJ#2328]「清华集训 2017」避难所 试题描述 "B君啊,你当年的伙伴都不在北京了,为什么你还在北京呢?" "大概是因为出了一些事故吧,否则这道题就不叫避难所 ...
- [LOJ#2327]「清华集训 2017」福若格斯
[LOJ#2327]「清华集训 2017」福若格斯 试题描述 小d是4xx9小游戏高手. 有一天,小d发现了一个很经典的小游戏:跳青蛙. 游戏在一个 \(5\) 个格子的棋盘上进行.在游戏的一开始,最 ...
随机推荐
- Android--MediaRecorder录音录像
前言 Android除了支持播放多媒体文件之外,还可以从对应的硬件中捕获多媒体,比如从麦克风录音.从摄像头录像等.本篇博客讲解一下Android下如何通过MediaRecorder进行录音以及录像的步 ...
- 在Mac上使用vs-code快速上手c语言学习(入门文,老鸟退散)
天下事,合久必分.分久必合,你肯定想不到当你逃离到Mac平台这么多年之后,有一天你会再用微软的产品来写代码 :) 其实微软的产品虽然用户体验总是做不到最好,但整体上的确拉低了行业的进入门槛,对于编程也 ...
- 阿里云Ubuntu安装图形界面与中文语言包
图形界面: http://blog.csdn.net/qq_37608398/article/details/78155568?locationNum=9&fps=1 安装中文: http:/ ...
- Struts2【UI标签、数据回显、资源国际化】
Struts2UI标签 Sturts2为了简化我们的开发,也为我们提供了UI标签...也就是显示页面的标签..... 但是呢,Struts2是服务端的框架,因此使用页面的标签是需要在服务器端解析然后再 ...
- Docker 网络之进阶篇
笔者在<Docker 基础 : 网络配置>一文中简单介绍了容器网络的基本用法,当时网络的基本使用方式还处于 --link 阶段.时过境迁,随着 docker 的快速发展,其网络架构也在不断 ...
- 【.NET Core项目实战-统一认证平台】第七章 网关篇-自定义客户端限流
[.NET Core项目实战-统一认证平台]开篇及目录索引 上篇文章我介绍了如何在网关上增加自定义客户端授权功能,从设计到编码实现,一步一步详细讲解,相信大家也掌握了自定义中间件的开发技巧了,本篇我们 ...
- git http服务免登录实现(免去每次请求用户名密码输入,Visual Studio可用)
最近用了Bonobo搭起了Git服务,弄了个批处理文件来避免每次都要输入用户名密码. 此脚本分为三个步骤:1.添加用户变量HOME:2.添加用户_netrc文件:3.添加windows普通凭据(因为V ...
- C# 爬虫----Cookies处理(Set-Cookie)
/// <summary> /// Cookie 助手 /// </summary> public class CookieHelper { /// <summary&g ...
- 安装屏保软件(Linux终端演示 “黑客帝国” 字母雨界面)和Linux修改管理员密码
1.Linux修改管理员密码:打开终端:1. 重启 reboot 2.进入内核登陆系统点击e3.进入系统救援界面,定位Linux16所在行,找到ro 后删除,在此位置添加一条命令: rw init= ...
- 【代码笔记】Web-CSS-CSS Display
一,效果图. 二,代码. <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...