UOJ #214 合唱队形 (概率期望计数、DP、Min-Max容斥)

红太狼 2021-12-03 07:49 327阅读 0赞

9个月的心头大恨终于切掉了!!!!
非常好的一道题,不知为何uoj上被点了70个差评。

题目链接: http://uoj.ac/problem/214

题目大意: 请自行阅读。

题解:

官方题解讲得相当清楚,这里补充一下自己的一些理解。

首先来看\(O(2^{n-m}\times poly(n,m))\)的做法。

一种理解方式是官方题解。

设\(s\)为总共的课程个数(\(n\)个字符串的总长度),\(p(S)\)表示结尾位置为集合\(S\)的串全部匹配一共需要完成多少个不同的课程。设\(f(t)\)表示\(t\)时刻整个过程没有终止的概率,\(prob(S,t)\)表示\(S\)集合结尾的串在\(t\)时刻或者之前已经完成匹配的概率。\(ans\)为答案。

\[ans=\sum^{+\inf}_{t=0} f(t)=\sum^{+\inf}_{t=0} \sum_{S, |S|\ge 0}(-1)^{|S|} prob(S,t)=\sum^{+\inf}_{t=0} \sum_{S,|S|\ge 0}(-1)^{|S|} \sum^{f(S)}_{i=0} (-1)^i{f(S)\choose i}(\frac{s-i}{s})^t\\ =\sum^{+\inf}_{t=0} \sum_{S,|S|\ge 0}(-1)^{|S|} \sum^{f(S)}_{i=1} (-1)^i{f(S)\choose i}(\frac{s-i}{s})^t\\ =\sum_{S,|S|\ge 0}(-1)^{|S|} \sum^{f(S)}_{i=0} (-1)^i{f(s)\choose i}\sum^{\inf}_{t=0}(\frac{s-i}{s})^t=\sum_{S,|S|\ge 0}(-1)^{|S|} \sum^{f(S)}_{i=0} (-1)^i{f(S)\choose i} \frac{s}{i}\]

计算即可。

其实还有另外一种理解方式。我们要求的是所有串匹配时间最小值的期望,根据Min-Max容斥,我们可以通过枚举子集转化成对每个子集求该子集内串匹配时间最大值的期望,然后转化成每一时刻没结束的概率。

然后来看更难的\(O(2^m\times poly(n,m))\)做法。

对于所有\(p(S)\)相同的\(S\), 其对答案的贡献没有区别。

因此对于所有\(k\),考虑计算\(p(S)=k\)的\(S\)的个数。

然后直接dp即可。

代码错误记录: 注意solution1如果\(n-m=16\)的话数组要开到\(2^{17}\).

代码

  1. #include<cstdio>
  2. #include<cstdlib>
  3. #include<cstring>
  4. #define llong long long
  5. using namespace std;
  6. const int N = 30;
  7. const int S = 26;
  8. const int P = 998244353;
  9. bool ok[N+3];
  10. int f[N+3][S+3];
  11. char str[N+3];
  12. char a[N+3];
  13. llong fact[2000003],finv[2000003],inv[2000003];
  14. int cnt[(1<<17)+3];
  15. int n,m,s;
  16. llong quickpow(llong x,llong y)
  17. {
  18. llong cur = x,ret = 1ll;
  19. for(int i=0; y; i++)
  20. {
  21. if(y&(1ll<<i)) {y-=(1ll<<i); ret = ret*cur%P;}
  22. cur = cur*cur%P;
  23. }
  24. return ret;
  25. }
  26. llong comb(llong x,llong y) {return x<0 || y<0 || x<y ? 0ll : fact[x]*finv[y]%P*finv[x-y]%P;}
  27. namespace Solution1
  28. {
  29. int ff[N+3][S+3];
  30. llong ans;
  31. void solve()
  32. {
  33. ans = 0ll;
  34. for(int i=1; i<(1<<(n-m+1)); i++)
  35. {
  36. llong cur = 0ll; bool gg = false;
  37. for(int j=0; j<=n-m; j++)
  38. {
  39. if(i&(1<<j))
  40. {
  41. for(int k=0; k<m; k++)
  42. {
  43. ff[j+k][a[k]-96] = true;
  44. if(f[j+k][a[k]-96]==false) {gg = true; break;}
  45. }
  46. if(gg==true) break;
  47. }
  48. }
  49. if(gg)
  50. {
  51. for(int j=0; j<n; j++)
  52. {
  53. for(int k=1; k<=S; k++)
  54. {
  55. ff[j][k] = false;
  56. }
  57. }
  58. continue;
  59. }
  60. int num = 0;
  61. for(int j=0; j<n; j++)
  62. {
  63. for(int k=1; k<=S; k++)
  64. {
  65. if(ff[j][k]==true) {num++;}
  66. }
  67. }
  68. for(int j=1; j<=num; j++)
  69. {
  70. llong tmp = (llong)s*inv[j]%P*comb(num,j)%P;
  71. if(!(j&1)) {cur = (cur+tmp)%P;}
  72. else {cur = (cur-tmp+P)%P;}
  73. }
  74. for(int j=0; j<n; j++)
  75. {
  76. for(int k=1; k<=S; k++)
  77. {
  78. ff[j][k] = false;
  79. }
  80. }
  81. if(cnt[i]&1) {ans = (ans-cur+P)%P;}
  82. else {ans = (ans+cur+P)%P;}
  83. }
  84. printf("%lld\n",ans);
  85. }
  86. }
  87. namespace Solution2
  88. {
  89. #define U ((1<<m)-1)
  90. const int M = 14;
  91. llong dp[N+3][(1<<M)+3][N*M+3];
  92. bool ff[N+3][M+3];
  93. void update(llong &x,llong y) {x = (x+y)%P;}
  94. void solve()
  95. {
  96. llong ans = 0ll;
  97. dp[m-1][0][0] = 1ll;
  98. for(int i=m-1; i<n; i++)
  99. {
  100. for(int j=0; j<(1<<m); j++)
  101. {
  102. bool okk = true;
  103. for(int k=0; k<m; k++) if((j&(1<<k)) && ok[i-k-1]==false) {okk = false; break;}
  104. if(!okk) continue;
  105. int p = 0;
  106. for(int k=0; k<m; k++)
  107. {
  108. if(j&(1<<k))
  109. {
  110. for(int l=0; l<m; l++)
  111. {
  112. ff[i-k-1-m+1+l][a[l]-96] = true;
  113. }
  114. }
  115. }
  116. for(int l=0; l<m; l++)
  117. {
  118. if(ff[i-m+1+l][a[l]-96]==false)
  119. {
  120. p++;
  121. }
  122. }
  123. for(int k=0; k<m; k++)
  124. {
  125. if(j&(1<<k))
  126. {
  127. for(int l=0; l<m; l++)
  128. {
  129. ff[i-k-1-m+1+l][a[l]-96] = false;
  130. }
  131. }
  132. }
  133. for(int k=0; k<=n*m; k++)
  134. {
  135. if(dp[i][j][k]==0) continue;
  136. update(dp[i+1][(j<<1)&U][k],dp[i][j][k]);
  137. update(dp[i+1][(j<<1|1)&U][k+p],P-dp[i][j][k]);
  138. dp[i][j][k] = 0ll;
  139. }
  140. }
  141. }
  142. for(int i=1; i<=n*m; i++)
  143. {
  144. llong coe = 0ll;
  145. for(int j=1; j<=i; j++)
  146. {
  147. llong tmp = s*inv[j]%P*comb(i,j)%P;
  148. if(j&1) {coe = (coe-tmp+P)%P;}
  149. else {coe = (coe+tmp)%P;}
  150. }
  151. llong sum = 0ll;
  152. for(int j=0; j<(1<<m); j++)
  153. {
  154. sum = (sum+dp[n][j][i])%P;
  155. dp[n][j][i] = 0ll;
  156. }
  157. ans = (ans+sum*coe)%P;
  158. }
  159. printf("%lld\n",ans);
  160. }
  161. }
  162. int main()
  163. {
  164. fact[0] = 1ll; for(int i=1; i<=2000000; i++) fact[i] = fact[i-1]*i%P;
  165. finv[2000000] = quickpow(fact[2000000],P-2); for(int i=1999999; i>=0; i--) finv[i] = finv[i+1]*(i+1)%P;
  166. for(int i=1; i<=2000000; i++) inv[i] = finv[i]*fact[i-1]%P;
  167. cnt[0] = 0; for(int i=1; i<(1<<17); i++) cnt[i] = cnt[i>>1]+(i&1);
  168. int T; scanf("%d",&T);
  169. while(T--)
  170. {
  171. scanf("%d%d",&n,&m); s = 0;
  172. for(int i=0; i<n; i++)
  173. {
  174. scanf("%s",str); int l = strlen(str);
  175. for(int j=0; j<l; j++)
  176. {
  177. f[i][str[j]-96] = true; s++;
  178. }
  179. }
  180. scanf("%s",a); bool exist = false;
  181. for(int i=m-1; i<n; i++)
  182. {
  183. ok[i] = true;
  184. for(int j=0; j<m; j++)
  185. {
  186. if(f[i-m+1+j][a[j]-96]==false) {ok[i] = false; break;}
  187. }
  188. if(ok[i]) {exist = true;}
  189. }
  190. if(exist==false) {printf("-1\n");}
  191. else
  192. {
  193. if(n-m<=16)
  194. {
  195. Solution1::solve();
  196. }
  197. else
  198. {
  199. Solution2::solve();
  200. }
  201. }
  202. for(int i=0; i<n; i++)
  203. {
  204. for(int j=1; j<=S; j++) f[i][j] = false;
  205. ok[i] = false;
  206. }
  207. }
  208. return 0;
  209. }

转载于:https://www.cnblogs.com/suncongbo/p/11027093.html

发表评论

表情:
评论列表 (有 0 条评论,327人围观)

还没有评论,来说两句吧...

相关阅读

    相关 P1091 合唱队

    题目描述 NNN位同学站成一排,音乐老师要请其中的(N−KN-KN−K)位同学出列,使得剩下的KKK位同学排成合唱队形。 合唱队形是指这样的一种队形:设K位同学从左到右