CTT=清华集训

题目大意

  有\(n\)个点,点权为\(a_i\),你要连接一条边,使该图变成一颗树。

  对于一种连边方案\(T\),设第\(i\)个点的度数为\(d_i\),那么这棵树的价值为:

\[val(T)=(\prod_{i=1}^na_i^{d_i}d_i^m)(\sum_{i=1}^nd_i^m)
\]

  求所有生成树的价值和\(\bmod 998244353\)

  \(n\leq 30000,m\leq 30\)

题解

  很容易想到prufer序列

  先把式子化简:

\[\begin{align}
ans&=(\prod_{i=1}^na_i^{d_i}d_i^m)(\sum_{i=1}^nd_i^m)\\
&=\sum_{i=1}^nd_i^m(\prod_{j=1}^na_j^{d_j}d_j^m)\\
&=\sum_{i=1}^na_i^{d_i}d_i^{2m}(\prod_{j=1,j!=i}^na_j^{d_j}d_j^m)\\
\end{align}
\]

  就是得到排列后选一个数\(i\),把贡献乘上\(d_i^m\)

  考虑最简单的做法,记\(f_{i,j}\)为前\(i\)个数,占了\(j\)个空,没有额外的选择一个数的贡献,\(g_{i,j}\)就是选了一个数。

  很容易得到DP式。这个DP是\(O(n^3)\)的,可以用FFT优化到\(O(n^2\log n)\)

  注意到\(m\)很小,还记得那个乘方转组合数和斯特林数的套路吗?

  先在prufer数列后面补上\(1\)~\(n\),这样写了数字\(i\)的格子个数就是\(d_i\)

  \(d_i^m\)就是用\(m\)种颜色染\(d_i\)个格子,每种颜色只能染一个格子,每个格子可以染多种颜色的方案数。

  因为染了颜色的格子很少,所以枚举写了数字\(i\)的格子中染了颜色的格子数量\(k\),如果染色的格子全在前面,方案数就是\(\binom{n-2-j}{k}\times S(m,k)\times k!\),贡献就是\(a_i^{k+1}\)。后面有就是\(\binom{n-2-j}{k}\times S(m,k+1)\times (k+1)!\)

  额外选一个数就把\(m\)改成\(2m\)

  \(f_{i,j},g_{i,j}\)的意义和以前类似,只不过改成了染色的格子个数是\(j\)。

  最后枚举染色的格子个数\(i\),没染色的格子可以随便填数,要乘上\({(\sum_{i=1}^na_i)}^{n-2-i}\)

  时间复杂度:\(O(n^2m)\)

  把组合数什么的全部拆开后就能得到一个比较漂亮的式子(其中\(F,G,A,B\)都是多项式):

\[\begin{align}
F_i&=F_{i-1}A\\
G_i&=G_{i-1}A+F_{i-1}B
\end{align}
\]

  可以用分治FFT优化。

  时间复杂度:\(O(nm\log^2 n)\)

代码

暴力

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<algorithm>
  4. #include<cstdlib>
  5. #include<ctime>
  6. #include<utility>
  7. #include<cmath>
  8. #include<functional>
  9. using namespace std;
  10. typedef long long ll;
  11. typedef unsigned long long ull;
  12. typedef pair<int,int> pii;
  13. typedef pair<ll,ll> pll;
  14. void sort(int &a,int &b)
  15. {
  16. if(a>b)
  17. swap(a,b);
  18. }
  19. void open(const char *s)
  20. {
  21. #ifndef ONLINE_JUDGE
  22. char str[100];
  23. sprintf(str,"%s.in",s);
  24. freopen(str,"r",stdin);
  25. sprintf(str,"%s.out",s);
  26. freopen(str,"w",stdout);
  27. #endif
  28. }
  29. int rd()
  30. {
  31. int s=0,c;
  32. while((c=getchar())<'0'||c>'9');
  33. do
  34. {
  35. s=s*10+c-'0';
  36. }
  37. while((c=getchar())>='0'&&c<='9');
  38. return s;
  39. }
  40. int upmin(int &a,int b)
  41. {
  42. if(b<a)
  43. {
  44. a=b;
  45. return 1;
  46. }
  47. return 0;
  48. }
  49. int upmax(int &a,int b)
  50. {
  51. if(b>a)
  52. {
  53. a=b;
  54. return 1;
  55. }
  56. return 0;
  57. }
  58. const ll p=998244353;
  59. ll h[62][62];
  60. ll f[2][60010];
  61. ll g[2][60010];
  62. ll a[30010];
  63. ll d[30010][62];
  64. ll fac[30010];
  65. ll ifac[30010];
  66. ll inv[30010];
  67. void add(ll &a,ll b)
  68. {
  69. a=(a+b)%p;
  70. }
  71. ll fp(ll a,ll b)
  72. {
  73. ll s=1;
  74. for(;b;b>>=1,a=a*a%p)
  75. if(b&1)
  76. s=s*a%p;
  77. return s;
  78. }
  79. ll getc(int x,int y)
  80. {
  81. if(x<y)
  82. return 0;
  83. return fac[x]*ifac[y]%p*ifac[x-y]%p;
  84. }
  85. ll s1[60010][62];
  86. ll s2[60010][62];
  87. ll s3[60010][62];
  88. ll s4[60010][62];
  89. int main()
  90. {
  91. freopen("a.in","r",stdin);
  92. freopen("a2.out","w",stdout);
  93. int n,m;
  94. scanf("%d%d",&n,&m);
  95. h[0][0]=1;
  96. int i,j,k;
  97. fac[0]=fac[1]=ifac[0]=ifac[1]=inv[0]=inv[1]=1;
  98. for(i=2;i<=2*m||i<=n;i++)
  99. {
  100. inv[i]=-p/i*inv[p%i]%p;
  101. fac[i]=fac[i-1]*i%p;
  102. ifac[i]=ifac[i-1]*inv[i]%p;
  103. }
  104. for(i=1;i<=2*m;i++)
  105. for(j=1;j<=2*m;j++)
  106. h[i][j]=(h[i-1][j-1]+h[i-1][j]*j)%p;
  107. for(i=0;i<=2*m;i++)
  108. for(j=0;j<=2*m;j++)
  109. h[i][j]=h[i][j]*fac[j]%p;
  110. for(i=1;i<=n;i++)
  111. {
  112. scanf("%lld",&a[i]);
  113. d[i][0]=1;
  114. for(j=1;j<=2*m;j++)
  115. d[i][j]=d[i][j-1]*a[i]%p;
  116. }
  117. for(i=1;i<=n;i++)
  118. for(j=0;j<=2*m;j++)
  119. {
  120. s1[i][j]=ifac[j]*d[i][j]%p*((h[m][j]+h[m][j+1])%p)%p;
  121. s2[i][j]=ifac[j]*d[i][j]%p*((h[2*m][j]+h[2*m][j+1])%p)%p;
  122. }
  123. f[0][0]=1;
  124. int t=0;
  125. for(i=0;i<n;i++)
  126. {
  127. t^=1;
  128. memset(f[t],0,sizeof f[t]);
  129. memset(g[t],0,sizeof g[t]);
  130. for(j=0;j<=n-2;j++)
  131. {
  132. for(k=0;k<=m&&k<=n-2-j;k++)
  133. {
  134. add(f[t][j+k],f[t^1][j]*s1[i+1][k]);
  135. add(g[t][j+k],g[t^1][j]*s1[i+1][k]);
  136. }
  137. for(k=0;k<=2*m&&k<=n-2-j;k++)
  138. add(g[t][j+k],f[t^1][j]*s2[i+1][k]);
  139. }
  140. }
  141. ll ans=0,s=0;
  142. for(i=1;i<=n;i++)
  143. s=(s+a[i])%p;
  144. for(i=0;i<=n-2;i++)
  145. ans=(ans+g[t][i]*ifac[n-2-i]%p*fp(s,n-2-i))%p;
  146. for(i=1;i<=n;i++)
  147. ans=ans*a[i]%p;
  148. ans=ans*fac[n-2]%p;
  149. ans=(ans+p)%p;
  150. printf("%lld\n",ans);
  151. return 0;
  152. }

正解

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<algorithm>
  4. #include<cstdlib>
  5. #include<ctime>
  6. #include<utility>
  7. #include<cmath>
  8. #include<functional>
  9. using namespace std;
  10. typedef long long ll;
  11. typedef unsigned long long ull;
  12. typedef pair<int,int> pii;
  13. typedef pair<ll,ll> pll;
  14. void sort(int &a,int &b)
  15. {
  16. if(a>b)
  17. swap(a,b);
  18. }
  19. void open(const char *s)
  20. {
  21. #ifndef ONLINE_JUDGE
  22. char str[100];
  23. sprintf(str,"%s.in",s);
  24. freopen(str,"r",stdin);
  25. sprintf(str,"%s.out",s);
  26. freopen(str,"w",stdout);
  27. #endif
  28. }
  29. int rd()
  30. {
  31. int s=0,c;
  32. while((c=getchar())<'0'||c>'9');
  33. do
  34. {
  35. s=s*10+c-'0';
  36. }
  37. while((c=getchar())>='0'&&c<='9');
  38. return s;
  39. }
  40. int upmin(int &a,int b)
  41. {
  42. if(b<a)
  43. {
  44. a=b;
  45. return 1;
  46. }
  47. return 0;
  48. }
  49. int upmax(int &a,int b)
  50. {
  51. if(b>a)
  52. {
  53. a=b;
  54. return 1;
  55. }
  56. return 0;
  57. }
  58. const int p=998244353;
  59. int fp(ll a,ll b)
  60. {
  61. ll s=1;
  62. for(;b;b>>=1,a=1ll*a*a%p)
  63. if(b&1)
  64. s=1ll*s*a%p;
  65. return s;
  66. }
  67. namespace ntt
  68. {
  69. const int g=3;
  70. int w1[150000];
  71. int w2[150000];
  72. int rev[150000];
  73. int n;
  74. void init(int m)
  75. {
  76. n=m;
  77. int i;
  78. for(i=2;i<=n;i<<=1)
  79. {
  80. w1[i]=fp(g,(p-1)/i);
  81. w2[i]=fp(w1[i],p-2);
  82. }
  83. rev[0]=0;
  84. for(i=1;i<n;i++)
  85. rev[i]=(rev[i>>1]>>1)|(i&1?n>>1:0);
  86. }
  87. void ntt(int *a,int t)
  88. {
  89. int u,v,w,wn;
  90. int i,j,k;
  91. for(i=0;i<n;i++)
  92. if(rev[i]<i)
  93. swap(a[i],a[rev[i]]);
  94. for(i=2;i<=n;i<<=1)
  95. {
  96. wn=(t==1?w1[i]:w2[i]);
  97. for(j=0;j<n;j+=i)
  98. {
  99. w=1;
  100. for(k=j;k<j+i/2;k++)
  101. {
  102. u=a[k];
  103. v=1ll*a[k+i/2]*w%p;
  104. a[k]=(u+v)%p;
  105. a[k+i/2]=(u-v)%p;
  106. w=1ll*w*wn%p;
  107. }
  108. }
  109. }
  110. if(t==-1)
  111. {
  112. int inv=fp(n,p-2);
  113. for(i=0;i<n;i++)
  114. a[i]=1ll*a[i]*inv%p;
  115. }
  116. }
  117. void copy(int *a,const vector<int> &b,int len)
  118. {
  119. int i;
  120. for(i=0;i<=len;i++)
  121. a[i]=b[i];
  122. for(i=len+1;i<n;i++)
  123. a[i]=0;
  124. }
  125. vector<int> back(int *a,int len)
  126. {
  127. vector<int> s;
  128. int i;
  129. for(i=0;i<=len;i++)
  130. s.push_back(a[i]);
  131. return s;
  132. }
  133. }
  134. int n,m;
  135. int h[62][62];
  136. int f[2][60010];
  137. int g[2][60010];
  138. int a[30010];
  139. int d[30010][62];
  140. int fac[30010];
  141. int ifac[30010];
  142. int inv[30010];
  143. void add(int &a,int b)
  144. {
  145. a=(a+b)%p;
  146. }
  147. int s1[60010][62];
  148. int s2[60010][62];
  149. auto gao(vector<int> &a,vector<int> &b,vector<int> &c,vector<int> &d)
  150. {
  151. static int a1[150000],a2[150000],a3[150000],a4[150000],s1[150000],s2[150000];
  152. int len=1;
  153. int n1=a.size()-1;
  154. int n2=b.size()-1;
  155. int n3=c.size()-1;
  156. int n4=d.size()-1;
  157. while(len<=2*n2||len<=2*n4)
  158. len<<=1;
  159. ntt::init(len);
  160. ntt::copy(a1,a,n1);
  161. ntt::copy(a2,b,n2);
  162. ntt::copy(a3,c,n3);
  163. ntt::copy(a4,d,n4);
  164. ntt::ntt(a1,1);
  165. ntt::ntt(a2,1);
  166. ntt::ntt(a3,1);
  167. ntt::ntt(a4,1);
  168. int i;
  169. for(i=0;i<len;i++)
  170. {
  171. s1[i]=1ll*a1[i]*a3[i]%p;
  172. s2[i]=(1ll*a1[i]*a4[i]+1ll*a2[i]*a3[i])%p;
  173. }
  174. ntt::ntt(s1,-1);
  175. ntt::ntt(s2,-1);
  176. return make_pair(ntt::back(s1,min(n-2,n1+n3)),ntt::back(s2,min(n-2,max(n1+n4,n2+n3))));
  177. }
  178. auto solve(int l,int r)
  179. {
  180. if(l==r)
  181. {
  182. vector<int> a(m+1),b(2*m+1);
  183. int i;
  184. for(i=0;i<=2*m;i++)
  185. {
  186. if(i<=m)
  187. a[i]=s1[l][i];
  188. b[i]=s2[l][i];
  189. }
  190. return make_pair(a,b);
  191. }
  192. int mid=(l+r)>>1;
  193. auto L=solve(l,mid);
  194. auto R=solve(mid+1,r);
  195. return gao(L.first,L.second,R.first,R.second);
  196. }
  197. int main()
  198. {
  199. open("a");
  200. scanf("%d%d",&n,&m);
  201. h[0][0]=1;
  202. int i,j,k;
  203. fac[0]=fac[1]=ifac[0]=ifac[1]=inv[0]=inv[1]=1;
  204. for(i=2;i<=2*m||i<=n;i++)
  205. {
  206. inv[i]=1ll*-p/i*inv[p%i]%p;
  207. fac[i]=1ll*fac[i-1]*i%p;
  208. ifac[i]=1ll*ifac[i-1]*inv[i]%p;
  209. }
  210. for(i=1;i<=2*m;i++)
  211. for(j=1;j<=2*m;j++)
  212. h[i][j]=(h[i-1][j-1]+1ll*h[i-1][j]*j)%p;
  213. for(i=0;i<=2*m;i++)
  214. for(j=0;j<=2*m;j++)
  215. h[i][j]=1ll*h[i][j]*fac[j]%p;
  216. for(i=1;i<=n;i++)
  217. {
  218. scanf("%d",&a[i]);
  219. d[i][0]=1;
  220. for(j=1;j<=2*m;j++)
  221. d[i][j]=1ll*d[i][j-1]*a[i]%p;
  222. }
  223. for(i=1;i<=n;i++)
  224. for(j=0;j<=2*m;j++)
  225. {
  226. s1[i][j]=1ll*ifac[j]*d[i][j]%p*((1ll*h[m][j]+h[m][j+1])%p)%p;
  227. s2[i][j]=1ll*ifac[j]*d[i][j]%p*((1ll*h[2*m][j]+h[2*m][j+1])%p)%p;
  228. }
  229. // f[0][0]=1;
  230. int t=0;
  231. // for(i=0;i<n;i++)
  232. // {
  233. // t^=1;
  234. // memset(f[t],0,sizeof f[t]);
  235. // memset(g[t],0,sizeof g[t]);
  236. // for(j=0;j<=n-2;j++)
  237. // {
  238. // for(k=0;k<=m;k++)
  239. // {
  240. // add(f[t][j+k],f[t^1][j]*s1[i+1][k]);
  241. // add(g[t][j+k],g[t^1][j]*s1[i+1][k]);
  242. // }
  243. // for(k=0;k<=2*m;k++)
  244. // add(g[t][j+k],f[t^1][j]*s2[i+1][k]);
  245. // }
  246. // }
  247. auto dp=solve(1,n);
  248. int ans=0,s=0;
  249. for(i=1;i<=n;i++)
  250. s=(s+a[i])%p;
  251. for(i=0;i<=n-2;i++)
  252. // ans=(ans+g[t][i]*ifac[n-2-i]%p*fp(s,n-2-i))%p;
  253. ans=(ans+1ll*dp.second[i]*ifac[n-2-i]%p*fp(s,n-2-i))%p;
  254. for(i=1;i<=n;i++)
  255. ans=1ll*ans*a[i]%p;
  256. ans=1ll*ans*fac[n-2]%p;
  257. ans=(ans+p)%p;
  258. printf("%d\n",ans);
  259. return 0;
  260. }

【BZOJ5119】【CTT2017】生成树计数 DP 分治FFT 斯特林数的更多相关文章

  1. 【BZOJ3456】轩辕朗的城市规划 无向连通图计数 CDQ分治 FFT 多项式求逆 多项式ln

    题解 分治FFT 设\(f_i\)为\(i\)个点组成的无向图个数,\(g_i\)为\(i\)个点组成的无向连通图个数 经过简单的推导(枚举\(1\)所在的连通块大小),有: \[ f_i=2^{\f ...

  2. BZOJ 2159: Crash 的文明世界(树形dp+第二类斯特林数+组合数)

    题意 给定一棵 \(n\) 个点的树和一个常数 \(k\) , 对于每个 \(i\) , 求 \[\displaystyle S(i) = \sum _{j=1} ^ {n} \mathrm{dist ...

  3. 【hdu4045】Machine scheduling(dp+第二类斯特林数)

    传送门 题意: 从\(n\)个人中选\(r\)个出来,但每两个人的标号不能少于\(k\). 再将\(r\)个人分为不超过\(m\)个集合. 问有多少种方案. 思路: 直接\(dp\)预处理出从\(n\ ...

  4. 【LOJ565】【LibreOJ Round #10】mathematican 的二进制 DP 分治FFT

    题目大意 有一个无限长的二进制串,初始时它的每一位都为 \(0\).现在有 \(m\) 个操作,其中第 \(i\) 个操作是将这个二进制串的数值加上 \(2^{a_i}\).我们称每次操作的代价是这次 ...

  5. bzoj1494 生成树计数 (dp+矩阵快速幂)

    题面欺诈系列... 因为一个点最多只能连到前k个点,所以只有当前的连续k个点的连通情况是对接下来的求解有用的 那么就可以计算k个点的所有连通情况,dfs以下发现k=5的时候有52种. 我们把它们用类似 ...

  6. 【bzoj2159】Crash 的文明世界(树形dp+第二类斯特林数)

    传送门 题意: 给出一颗\(n\)个结点的树,对于每个结点输出其答案,每个结点的答案为\(ans_x=\sum_{i=1}^ndis(x,i)^k\). 思路: 我们对于每个结点将其答案展开: \[ ...

  7. UOJ#335. 【清华集训2017】生成树计数 多项式,FFT,下降幂,分治

    原文链接www.cnblogs.com/zhouzhendong/p/UOJ335.html 前言 CLY大爷随手切这种题. 日常被CLY吊打系列. 题解 首先从 pruffer 编码的角度考虑这个问 ...

  8. BZOJ 4555: [Tjoi2016&Heoi2016]求和 [分治FFT 组合计数 | 多项式求逆]

    4555: [Tjoi2016&Heoi2016]求和 题意:求\[ \sum_{i=0}^n \sum_{j=0}^i S(i,j)\cdot 2^j\cdot j! \\ S是第二类斯特林 ...

  9. 【XSY2666】排列问题 DP 容斥原理 分治FFT

    题目大意 有\(n\)种颜色的球,第\(i\)种有\(a_i\)个.设\(m=\sum a_i\).你要把这\(m\)个小球排成一排.有\(q\)个询问,每次给你一个\(x\),问你有多少种方案使得相 ...

随机推荐

  1. Python中Socket粘包问题的解决

    服务器端 import socket import subprocess import struct server = socket.socket() ip_port = ("192.168 ...

  2. JS 原型与原型链

    图解: 一.普通对象 跟 函数对象 JavaScript 中,一切皆对象.但对象也有区别,分为 普通对象 跟 函数对象,Object 和 Function 是 JavaScript 自带的函数对象. ...

  3. Karen and Game CodeForces - 816C (暴力+构造)

    On the way to school, Karen became fixated on the puzzle game on her phone! The game is played as fo ...

  4. 我们为什么要使用List和Set(List,Set详解)

    1.集合概述 类图 集合和数组的区别? 集合基本方法 集合特有的遍历方式? public static void main(String[] args) { //创建集合对象 Collection c ...

  5. CSS小东西

    1.表格列自动均分 table-layout:fixed; 2.单元格内容自动换行 word-wrap:break-word;

  6. mysql问题汇总——持续更新

    1.this is incompatible with sql_mode=only_full_group_by set @@sql_mode='STRICT_TRANS_TABLES,NO_ZERO_ ...

  7. [转帖]Docker的数据管理(volume/bind mount/tmpfs)

    Docker(十五)-Docker的数据管理(volume/bind mount/tmpfs) https://www.cnblogs.com/zhuochong/p/10069719.html do ...

  8. 将表单数据转换为json代码分享

    <body> <form action="#" method="post" id="form1"> <inpu ...

  9. oracle查看表结构命令desc

  10. Oracle 备份表数据

    --备份表数据 select * from t_owners; --创建备份表 create table t_owners_copy ( id number, name ), addressid nu ...