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

题目描述

在一个 \(s\) 个点的图中,存在 \(s-n\) 条边,使图中形成了 \(n\) 个连通块,第 \(i\) 个连通块中有 \(a_i\) 个点。

现在我们需要再连接 \(n-1\) 条边,使该图变成一棵树。对一种连边方案,设原图中第 \(i\) 个连通块连出了 \(d_i\) 条边,那么这棵树 \(T\) 的价值为:

\[\mathrm{val}(T) = \left(\prod_{i=1}^{n} {d_i}^m\right)\left(\sum_{i=1}^{n} {d_i}^m\right)
\]

你的任务是求出所有可能的生成树的价值之和,对 \(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=(n-2)!\prod_{i=1}^n\frac{{a_i}^{d_i+1}}{d_i!}
\]

先考虑对式子进行变形

\[\begin{align}
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}
\]

\[Ans'=\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!}
\]

考虑用生成函数解决:

\[A(x)=\sum_{i=0}^n\frac{i^{2m}}{i!}x^i\\
B(x)=\sum_{i=0}^n\frac{i^m}{i!}x^i
\]

则\(Ans'\)的生成函数为

\[\sum_{i=1}^nA(a_i)\prod_{j=1,j\neq i}^nB(a_j)\\
=\sum_{i=1}^n\frac{A(a_i)}{B(a_i)}\prod_{j=1}^nB(a_j)
\]

对于\(\prod_{j=1}^nB(a_j)\),我们的一般套路是将其写成

\[\exp(\ln(\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)}\)我们也 用相同的处理方式。

所以:

\[Ans'=\sum_{i=1}^n\frac{A}{B}(a_i)\exp(\sum_{j=1}^n\ln(B(a_j)))
\]

现在的问题是如何求出

\[\sum_{i=1}^n{a_i}^k
\]

考虑\(\ln(x)\)的取\(x_0=1\)时的泰勒展开形式

\[\ln(x)=\sum_{i=0}\frac{\ln^{[i](1)}}{i!}(x-1)^i\\
=\sum_{i=1}\frac{(-1)^{i-1}}{i}(x-1)^i
\]

所以:

\[\ln(1+a_jx)=\sum_{i=1}\frac{(-1)^{i-1}{a_j}^i}{i}x^i
\]

那么我们只需要求出

\[\sum_{i=1}^n\ln(a_i)
\]

就行了。

\[\sum_{i=1}^n\ln(a_i)=\ln(\prod_{i=1}^n(1+a_ix))
\]

\(\prod_{i=1}^n(1+a_ix)\)可以用分治\(NTT\)求出。

代码:

  1. #include<bits/stdc++.h>
  2. #define ll long long
  3. #define N 200005
  4. using namespace std;
  5. 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;}
  6. const ll mod=998244353;
  7. ll ksm(ll t,ll x) {
  8. ll ans=1;
  9. for(;x;x>>=1,t=t*t%mod)
  10. if(x&1) ans=ans*t%mod;
  11. return ans;
  12. }
  13. int n,m;
  14. int a[N];
  15. void NTT(ll *a,int d,int flag) {
  16. int n=1<<d;
  17. static int rev[N<<2];
  18. static ll G=3;
  19. for(int i=0;i<n;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<d-1);
  20. for(int i=0;i<n;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
  21. for(int s=1;s<=d;s++) {
  22. int len=1<<s,mid=len>>1;
  23. ll w=flag==1?ksm(G,(mod-1)/len):ksm(G,mod-1-(mod-1)/len);
  24. for(int i=0;i<n;i+=len) {
  25. ll t=1;
  26. for(int j=0;j<mid;j++,t=t*w%mod) {
  27. ll u=a[i+j],v=a[i+j+mid]*t%mod;
  28. a[i+j]=(u+v)%mod;
  29. a[i+j+mid]=(u-v+mod)%mod;
  30. }
  31. }
  32. }
  33. if(flag==-1) {
  34. ll inv=ksm(n,mod-2);
  35. for(int i=0;i<n;i++) a[i]=a[i]*inv%mod;
  36. }
  37. }
  38. ll A[N<<2],B[N<<2];
  39. ll inv[N<<2];
  40. ll f[N<<2],g[N<<2];
  41. void Inv(ll *inv,ll *a,int d) {
  42. static ll A[N<<3];
  43. if(d==0) {
  44. inv[0]=ksm(a[0],mod-2);
  45. return ;
  46. }
  47. Inv(inv,a,d-1);
  48. for(int i=0;i<1<<d;i++) A[i]=a[i];
  49. for(int i=1<<d;i<1<<d+1;i++) inv[i]=A[i]=0;
  50. NTT(A,d+1,1);
  51. NTT(inv,d+1,1);
  52. for(int i=0;i<1<<d+1;i++) {
  53. inv[i]=(2*inv[i]-A[i]*inv[i]%mod*inv[i]%mod+mod)%mod;
  54. }
  55. NTT(inv,d+1,-1);
  56. for(int i=1<<d;i<1<<d+1;i++) inv[i]=0;
  57. }
  58. void Der(ll *a,int d) {
  59. int n=1<<d;
  60. for(int i=0;i<n-1;i++) a[i]=(i+1)*a[i+1]%mod;
  61. a[n-1]=0;
  62. }
  63. void Int(ll *a,int d) {
  64. int n=1<<d;
  65. for(int i=n-1;i>0;i--) a[i]=ksm(i,mod-2)*a[i-1]%mod;
  66. a[0]=0;
  67. }
  68. ll ln[N<<2];
  69. void Ln(ll *ln,ll *a,int d) {
  70. static ll der[N<<2];
  71. for(int i=0;i<1<<d+1;i++) der[i]=0;
  72. for(int i=0;i<1<<d;i++) der[i]=a[i];
  73. Inv(inv,a,d);
  74. Der(der,d);
  75. NTT(inv,d+1,1),NTT(der,d+1,1);
  76. for(int i=0;i<1<<d+1;i++) ln[i]=inv[i]*der[i]%mod;
  77. NTT(ln,d+1,-1);
  78. for(int i=1<<d;i<1<<d+1;i++) ln[i]=0;
  79. Int(ln,d);
  80. for(int i=1<<d;i<1<<d+1;i++) ln[i]=0;
  81. }
  82. ll ex[N<<2];
  83. void Exp(ll *exp,ll *a,int d) {
  84. static ll A[N<<2],B[N<<2];
  85. if(d==0) {
  86. exp[0]=1;
  87. return ;
  88. }
  89. Exp(exp,a,d-1);
  90. for(int i=0;i<1<<d;i++) A[i]=a[i];
  91. for(int i=1<<d;i<1<<d+1;i++) exp[i]=A[i]=0;
  92. Ln(B,exp,d);
  93. NTT(exp,d+1,1);
  94. NTT(B,d+1,1);
  95. NTT(A,d+1,1);
  96. for(int i=0;i<1<<d+1;i++) {
  97. exp[i]=exp[i]*(1-B[i]+A[i]+mod)%mod;
  98. }
  99. NTT(exp,d+1,-1);
  100. for(int i=1<<d;i<1<<d+1;i++) exp[i]=0;
  101. }
  102. void solve(int l,int r,ll *a) {
  103. static ll A[N<<2],B[N<<2];
  104. if(l==r) return ;
  105. int mid=l+r>>1;
  106. solve(l,mid,a),solve(mid+1,r,a);
  107. int d=ceil(log2(r-l+2));
  108. for(int i=0;i<1<<d;i++) A[i]=B[i]=0;
  109. for(int i=l;i<=mid;i++) A[i-l+1]=a[i];
  110. for(int i=mid+1;i<=r;i++) B[i-mid]=a[i];
  111. A[0]=B[0]=1;
  112. NTT(A,d,1),NTT(B,d,1);
  113. for(int i=0;i<1<<d;i++) A[i]=A[i]*B[i]%mod;
  114. NTT(A,d,-1);
  115. for(int i=l;i<=r;i++) a[i]=A[i-l+1];
  116. }
  117. ll summ[N];
  118. ll cal(int k) {
  119. ll ans=0;
  120. for(int i=1;i<=n;i++) (ans+=ksm(a[i],k))%=mod;
  121. return ans;
  122. }
  123. ll tem[N<<2];
  124. ll fac[N],ifac[N];
  125. int main() {
  126. n=Get(),m=Get();
  127. fac[0]=1;
  128. for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
  129. ifac[n]=ksm(fac[n],mod-2);
  130. for(int i=n-1;i>=0;i--) ifac[i]=ifac[i+1]*(i+1)%mod;
  131. for(int i=1;i<=n;i++) a[i]=Get();
  132. for(int i=1;i<=n;i++) summ[i]=a[i];
  133. int d=ceil(log2(2*n+1));
  134. solve(1,n,summ);
  135. summ[0]=1;
  136. Ln(ln,summ,d);
  137. memcpy(summ,ln,sizeof(summ));
  138. summ[0]=n;
  139. for(int i=1;i<=n;i++) {
  140. if(!(i&1)) summ[i]=summ[i]*(mod-1)%mod;
  141. summ[i]=summ[i]*i%mod;
  142. }
  143. for(int i=0;i<=n;i++) {
  144. A[i]=ksm(i+1,2*m)*ifac[i]%mod;
  145. B[i]=ksm(i+1,m)*ifac[i]%mod;
  146. }
  147. Ln(ln,B,d);
  148. for(int i=0;i<1<<d;i++) ln[i]=ln[i]*summ[i]%mod;
  149. for(int i=n;i<1<<d;i++) ln[i]=0;
  150. Exp(g,ln,d);
  151. for(int i=n;i<=1<<d;i++) g[i]=0;
  152. Inv(inv,B,d);
  153. for(int i=n;i<1<<d;i++) inv[i]=0;
  154. NTT(inv,d,1),NTT(A,d,1);
  155. for(int i=0;i<1<<d;i++) f[i]=inv[i]*A[i]%mod;
  156. NTT(f,d,-1);
  157. for(int i=0;i<1<<d;i++) f[i]=f[i]*summ[i]%mod;
  158. for(int i=n;i<1<<d;i++) f[i]=0;
  159. NTT(f,d,1),NTT(g,d,1);
  160. for(int i=0;i<1<<d;i++) f[i]=f[i]*g[i]%mod;
  161. NTT(f,d,-1);
  162. ll ans=fac[n-2];
  163. for(int i=1;i<=n;i++) ans=ans*a[i]%mod;
  164. ans=ans*f[n-2]%mod;
  165. cout<<ans;
  166. return 0;
  167. }

Loj 2320.「清华集训 2017」生成树计数的更多相关文章

  1. 【LOJ】#2320. 「清华集训 2017」生成树计数

    题解 我,理解题解,用了一天 我,卡常数,又用了一天 到了最后,我才发现,我有个加法取模,写的是while(c >= MOD) c -= MOD 我把while改成if,时间,少了 六倍. 六倍 ...

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

    由于菜鸡的我实在是没学会上升幂下降幂那一套理论,这里用的是完全普通多项式的做法. 要是有大佬愿意给我讲讲上升幂下降幂那一套东西,不胜感激orz! 首先可以想到prufer序列,如果不会的话可以左转百度 ...

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

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

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

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

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

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

  6. [LOJ#2330]「清华集训 2017」榕树之心

    [LOJ#2330]「清华集训 2017」榕树之心 试题描述 深秋.冷风吹散了最后一丝夏日的暑气,也吹落了榕树脚下灌木丛的叶子.相识数年的Evan和Lyra再次回到了小时候见面的茂盛榕树之下.小溪依旧 ...

  7. [LOJ#2329]「清华集训 2017」我的生命已如风中残烛

    [LOJ#2329]「清华集训 2017」我的生命已如风中残烛 试题描述 九条可怜是一个贪玩的女孩子. 这天她在一堵墙钉了 \(n\) 个钉子,第 \(i\) 个钉子的坐标是 \((x_i,y_i)\ ...

  8. [LOJ#2328]「清华集训 2017」避难所

    [LOJ#2328]「清华集训 2017」避难所 试题描述 "B君啊,你当年的伙伴都不在北京了,为什么你还在北京呢?" "大概是因为出了一些事故吧,否则这道题就不叫避难所 ...

  9. [LOJ#2327]「清华集训 2017」福若格斯

    [LOJ#2327]「清华集训 2017」福若格斯 试题描述 小d是4xx9小游戏高手. 有一天,小d发现了一个很经典的小游戏:跳青蛙. 游戏在一个 \(5\) 个格子的棋盘上进行.在游戏的一开始,最 ...

随机推荐

  1. selenium和webdriver区别

    接触selenium大概半年时间了.从开始的预研,简单的写个流程到后期的自动化框架的开发,因为本人不属于代码方面的大牛,一直的边研究边做.逐步深入学习.近期发现自己对本身selenium的发展还存在困 ...

  2. kubernetes进阶之六:StatefulSet & DaemonSet

    StatefulSet(有状态): StatefulSet适合持久性的应用程序,有唯一的网络标识符(IP),持久存储,有序的部署,拓展,删除和滚动更新. 在Kubernetes系统中,Pod的管理对象 ...

  3. springboot情操陶冶-@ConfigurationProperties注解解析

    承接前文springboot情操陶冶-@Configuration注解解析,本文将在前文的基础上阐述@ConfigurationProperties注解的使用 @ConfigurationProper ...

  4. JVM(五)垃圾回收器的前世今生

    全文共 2195 个字,读完大约需要 8 分钟. 如果垃圾回收的算法属于内存回收的方法论的话,那本文讨论的垃圾回收器就属于内存回收的具体实现. 因为不同的厂商(IBM.Oracle),实现的垃圾回收器 ...

  5. 利用BGP虚拟下一跳实现链路负载均衡

    最近针对BGP链路负载均衡方案“虚拟下一跳”进行了总结,现将总结的PPT贴上来.

  6. 利用 c# linq 实现跨数据库的联合查询

    有个需求就是,我们要查询的信息分布在两个不同的数据库中,通过外键相互关联起来,然后返回datatable在前端展示内容. 根据需求我们可以考虑c#的linq 先在从不同的数据中获取相关的datatab ...

  7. C# 批量删除Word超链接

    对于Word文档中包含较多的超链接,如果一个个来删除很花费时间和精力,本篇文章将提供一种可用于批量删除Word中的超链接的方法.这里的超链接可以是页眉页脚处的超链接.正文中的超链接.表格中的超链接.文 ...

  8. RabbitMQ如何工作和RabbitMQ核心概念

    RabbitMQ是一个开源的消息代理软件.它接受来自生产者的消息并将其传递给消费者.它就像一个中间人,可以用来减少Web应用程序服务器的负载和交付时间. RabbitMQ如何工作 让我们简要介绍一下R ...

  9. HTTPS 站点的性能优化

    HTTPS 站中的几大难题 性能,包括: HTTPS需要多次握手,因此网络耗时变长,用户从HTTP跳转到HTTPS需要一些时间: HTTPS要做RSA校验,这会影响到设备性能: 所有CDN节点要支持H ...

  10. 通过hash实现前端路由

    router.js //构造函数 function Router() { this.routes = {}; this.currentUrl = ''; } Router.prototype.rout ...