题意就不用讲了吧……

鸡你太美!!!

题意:

有 \(4\) 种喜好不同的人,分别最爱唱、跳、 \(rap\)、篮球,他们个数分别为 \(A,B,C,D\) ,现从他们中挑选出 \(n\) 个人并进行排列,规定不能出现喜爱唱、跳、 rap、篮球的人在序列中依次出现,问合法方案数。

下文将喜爱唱、跳、 \(rap\)、篮球的人依次出现的区间称为聚集区间,长度为 \(4\)。

思路(容斥原理 + 生成函数 + \(\mathcal{NTT}\))

首先,我们可以发现如果顺着求方案数并不好求。秉持顺难逆易的原则,我们可以考虑容斥,令 \(f(k)\) 表示在长度为 \(n\) 的序列中出现了至少 \(k\) 个聚集区间的方案数,进而我们得出:\(Answer=\displaystyle\sum_{k}(-1)^{k}f(k)\)。

接下来,我们的目标就是求出 \(f(k)\)。我们可以设这 \(k\) 个聚集区间的起始位置 \(i\),并把 \(i,i+1,i+2,i+3\) 缩为一个点,这样就一共剩下 \(n-3k\) 个点,接着,我们就可以从这 \(n-3k\) 个点中选取不在聚集区间内的点,这样的点一共有 \(n-4k\) 个,所以方案数为 \(\binom{n-3k}{n-4k}=\binom{n-3k}{k}\)。

我们设 \(S(a,b,c,d,n)\) 表示 \(4\) 种人的数量分别有 \(a,b,c,d\) 个,从中挑选出 \(n\) 个人并进行排列的方案数。所以,\(f(k)=\binom{n-3k}{k}S(A-k,B-k,C-k,D-k,n-4k)\)

随后,我们把注意力放在 \(S(a,b,c,d,n)\) 上,可以发现这与指数型生成函数 \(EGF\) 的应用场景非常类似。所以,我们可以写出每种人的生成函数,例如第一种人的生成函数为 \(\displaystyle\sum_{k}^{a}\dfrac{x^k}{k!}\)。接着,我们将这四种人的生成函数进行卷积,而其中的第 \(n\) 项的系数就是答案,即 \([x^{n}]\displaystyle\sum_{k}^{a}\dfrac{x^k}{k!}\displaystyle\sum_{k}^{b}\dfrac{x^k}{k!}\displaystyle\sum_{k}^{c}\dfrac{x^k}{k!}\displaystyle\sum_{k}^{d}\dfrac{x^k}{k!}\)

综上,\(Answer=\displaystyle\sum_{k}(-1)^{k}f(k)=\displaystyle\sum_{k}(-1)^{k}\binom{n-3k}{k}S(A-k,B-k,C-k,D-k,n-4k)\)

时间复杂度

如果卷积时使用 \(\mathcal{NTT}\),时间复杂度约为 \(n^2\log_2n\)。但由于数据不强,时限宽松,暴力进行卷积也可通过,时间复杂度约为 \(n^3\)。

听说还有使用其它方法 \(AC\) 的 \(n\sqrt{n}\) 的算法,蒟蒻不会太强了%%%

代码实现:

  1. #include <map>
  2. #include <cmath>
  3. #include <ctime>
  4. #include <queue>
  5. #include <cstdio>
  6. #include <vector>
  7. #include <cstdlib>
  8. #include <cstring>
  9. #include <iostream>
  10. #include <algorithm>
  11. using namespace std;
  12. #define LL long long
  13. #define Int register int
  14. #define Lc(x) Child[x][0]
  15. #define Rc(x) Child[x][1]
  16. #define Swap(a, b) a ^= b ^= a ^= b
  17. #define Abs(x) ((x) < 0 ? -(x) : (x))
  18. #define Max(x, y) ((x) < (y) ? (y) : (x))
  19. #define Min(x, y) ((x) < (y) ? (x) : (y))
  20. #define Isdigit(ch) (ch >= '0' and ch <= '9')
  21. const int MAXN = 1e3 + 10;
  22. const double Pi = acos (-1.0);
  23. const LL Mod = 998244353, G = 3, Inv2 = 499122177, INF = 1LL << 60;
  24. inline LL Read () {
  25. LL f = 0, x = 0;
  26. char ch = getchar ();
  27. while (!isdigit (ch) ) {
  28. f |= (ch == '-'), ch = getchar ();
  29. }
  30. while (isdigit (ch) ) {
  31. x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar ();
  32. }
  33. return f ? -x : x;
  34. }
  35. inline void Write (const LL &x) {
  36. if (x < 0 ) {
  37. putchar ('-'), Write (-x);
  38. return ;
  39. }
  40. if (x > 9 ) {
  41. Write (x / 10);
  42. }
  43. putchar ((x % 10) ^ 48);
  44. return ;
  45. }
  46. LL Answer;
  47. int n, a, b, c, d;
  48. LL Fact[MAXN], Inv[MAXN], Pow[MAXN], Sum1[MAXN], Sum2[MAXN];
  49. inline LL Qkpow (LL Base, LL x) {
  50. LL Total = 1;
  51. while (x ) {
  52. if (x & 1 ) {
  53. Total = Total * Base % Mod;
  54. }
  55. Base = Base * Base % Mod, x >>= 1;
  56. }
  57. return Total;
  58. }
  59. inline LL Getinv (const LL x) {
  60. return Qkpow (x, Mod - 2);
  61. }
  62. inline LL Getbinom (const int n, const int m) {
  63. return Fact[n] * Inv[m] % Mod * Inv[n - m] % Mod;
  64. }
  65. inline LL Clac (const int a, const int b, const int c, const int d, const int n) {
  66. Int i, j;
  67. for (i = 0; i <= n; ++ i ) {
  68. Sum1[i] = Sum2[i] = 0;
  69. }
  70. Sum1[0] = 1;
  71. for (i = 0; i <= n; ++ i ) {
  72. for (j = 0; j <= a and i + j <= n; ++ j ) {
  73. Sum2[i + j] = (Sum2[i + j] + Sum1[i] * Inv[i] % Mod * Fact[i + j] % Mod * Inv[j] % Mod) % Mod;
  74. }
  75. }
  76. for (i = 0; i <= n; ++ i ) {
  77. Sum1[i] = Sum2[i], Sum2[i] = 0;
  78. // printf ("%lld ", Sum1[i]);
  79. }
  80. // putchar ('\n');
  81. for (i = 0; i <= n; ++ i ) {
  82. for (j = 0; j <= b and i + j <= n; ++ j ) {
  83. Sum2[i + j] = (Sum2[i + j] + Sum1[i] * Inv[i] % Mod * Fact[i + j] % Mod * Inv[j] % Mod) % Mod;
  84. }
  85. }
  86. for (i = 0; i <= n; ++ i ) {
  87. Sum1[i] = Sum2[i], Sum2[i] = 0;
  88. // printf ("%lld ", Sum1[i]);
  89. }
  90. // putchar ('\n');
  91. for (i = 0; i <= n; ++ i ) {
  92. for (j = 0; j <= c and i + j <= n; ++ j ) {
  93. Sum2[i + j] = (Sum2[i + j] + Sum1[i] * Inv[i] % Mod * Fact[i + j] % Mod * Inv[j] % Mod) % Mod;
  94. }
  95. }
  96. for (i = 0; i <= n; ++ i ) {
  97. Sum1[i] = Sum2[i], Sum2[i] = 0;
  98. // printf ("%lld ", Sum1[i]);
  99. }
  100. // putchar ('\n');
  101. for (i = 0; i <= n; ++ i ) {
  102. for (j = 0; j <= d and i + j <= n; ++ j ) {
  103. Sum2[i + j] = (Sum2[i + j] + Sum1[i] * Inv[i] % Mod * Fact[i + j] % Mod * Inv[j] % Mod) % Mod;
  104. }
  105. }
  106. for (i = 0; i <= n; ++ i ) {
  107. Sum1[i] = Sum2[i], Sum2[i] = 0;
  108. // printf ("%lld ", Sum1[i]);
  109. }
  110. // putchar ('\n');
  111. return Sum1[n];
  112. }
  113. signed main () {
  114. n = Read (), a = Read (), b = Read (), c = Read (), d = Read ();
  115. Int i;
  116. for (i = Pow[0] = Fact[0] = 1; i <= n; ++ i ) {
  117. Fact[i] = Fact[i - 1] * i % Mod, Pow[i] = Pow[i - 1] * 4 % Mod;
  118. }
  119. Inv[n] = Getinv (Fact[n]);
  120. for (i = n - 1; ~i; -- i ) {
  121. Inv[i] = Inv[i + 1] * (i + 1) % Mod;
  122. }
  123. if (a == b and b == c and c == d ) {
  124. for (i = 0; i <= n / 4; ++ i ) {
  125. Answer = (Answer + (i & 1 ? -1 : 1) * Pow[n - 4 * i] * Getbinom (n - 3 * i, i) % Mod + Mod) % Mod;;
  126. }
  127. Write (Answer);
  128. return 0;
  129. }
  130. for (i = 0; i <= n / 4 and i <= a and i <= b and i <= c and i <= d; ++ i ) {
  131. // printf ("---------i = %d---------\n", i);
  132. Answer = (Answer + (i & 1 ? -1 : 1) * Clac (a - i, b - i, c - i, d - i, n - 4 * i) * Getbinom (n - 3 * i, i) % Mod + Mod) % Mod;;
  133. // Write (Answer), putchar ('\n');
  134. }
  135. Write (Answer);
  136. return 0;
  137. }

「TJOI2019」唱、跳、rap 和篮球 题解的更多相关文章

  1. 「TJOI2019」唱、跳、rap 和篮球

    题目链接 题目分析 据说这是一道生成函数题 看到限制条件,我们首先想到的就是对有多少组讨论cxk的人进行容斥.然后就是求剩下的人随便放有多少种方法了.考虑现在每种剩\(a,b,c,d\)人,还需要排\ ...

  2. [bzoj5510]唱跳rap和篮球

    显然答案可以理解为有(不是仅有)0对情况-1对情况+2对情况-- 考虑这个怎么计算,先计算这t对情况的位置,有c(n-3t,t)种情况(可以理解为将这4个点缩为1个,然后再从中选t个位置),然后相当于 ...

  3. 【LOJ】#3109. 「TJOI2019」甲苯先生的线段树

    LOJ#3109. 「TJOI2019」甲苯先生的线段树 发现如果枚举路径两边的长度的话,如果根节点的值是$x$,左边走了$l$,右边走了$r$ 肯定答案会是$(2^{l + 1} + 2^{r + ...

  4. LG5337/BZOJ5508 「TJOI2019」甲苯先生的字符串 线性动态规划+矩阵加速

    问题描述 LG5337 BZOJ5508 题解 设\(opt_{i,j}(i \in [1,n],j \in [1,26])\)代表区间\([1,i]\),结尾为\(j\)的写法. 设\(exist_ ...

  5. LG5338/BZOJ5509/LOJ3105 「TJOI2019」甲苯先生的滚榜 Treap

    问题描述 LG5338 LOJ3105 BZOJ5509 题解 建立一棵\(\mathrm{Treap}\),把原来的\(val\)换成两个值\(ac,tim\) 原来的比较\(val_a<va ...

  6. LOJ#3104「TJOI2019」甲苯先生的字符串

    题目描述 一天小甲苯得到了一条神的指示,他要把神的指示写下来,但是又不能泄露天机,所以他要用一种方法把神的指示记下来. 神的指示是一个字符串,记为字符串 \(s_1\),\(s_1\) 仅包含小写字母 ...

  7. 「TJOI2019」大中锋的游乐场

    题目链接 问题分析 比较明显的最短路模型.需要堆优化的dij.建图的时候注意细节就好. 参考程序 #include <bits/stdc++.h> #define LL long long ...

  8. 「TJOI2019」甲苯先生的滚榜

    题目链接 问题分析 参照数据范围,我们需要一个能够在\(O(n\log n)\)复杂度内维护有序数列的数据结构.那么平衡树是很好的选择.参考程序中使用带旋Treap. 参考程序 #pragma GCC ...

  9. 将Android手机无线连接到Ubuntu实现唱跳Rap

    您想要将Android设备连接到Ubuntu以传输文件.查看Android通知.以及从Ubuntu桌面发送短信 – 你会怎么做?将文件从手机传输到PC时不要打电话给自己:使用GSConnect就可以. ...

随机推荐

  1. STM32中STD、HAL、LL库比较

    ST为开发者提供了标准外设库(STD库).HAL库.LL库 三种.前两者都是常用的库,后面的LL库是ST新添加的,随HAL源码包一起提供,目前支持的芯片也偏少. 标准外设库(Standard Peri ...

  2. libcurl库(C++)快速使用

    ---恢复内容开始--- libcurl是一个跨平台的网络协议库,支持http, https, ftp, gopher, telnet, dict, file, 和ldap 协议.libcurl同样支 ...

  3. c++ vector用法详解

    1. 定义: 向量(Vector)是一个封装了动态大小数组的顺序容器(Sequence Container)可以认为是一个动态数组,其中一个vector中的所有对象都必须是同一种类型的. 2. 构造函 ...

  4. 关于torch.nn.LSTM()的输入和输出

    主角torch.nn.LSTM() 初始化时要传入的参数 | Args: | input_size: The number of expected features in the input `x` ...

  5. Leetcode4. 寻找两个正序数组的中位数

    > 简洁易懂讲清原理,讲不清你来打我~ 输入两个递增数组,输出中位数![在这里插入图片描述](https://img-blog.csdnimg.cn/25550994642144228e9862 ...

  6. 关于java.lang.IllegalMonitorStateException异常说明(四)

    1.异常原因及解释 首先你要了解这个异常为什么会抛出,这个异常会在三种情况下抛出:1>当前线程不含有当前对象的锁资源的时候,调用obj.wait()方法;2>当前线程不含有当前对象的锁资源 ...

  7. odoo12学习之javascript

    本文来源:https://www.jianshu.com/p/1a47fac01077 Odoo12 Javascript 参考指南   本文介绍了odoo javascript框架.从代码行的角度来 ...

  8. 常用PLC与ifix/intouch驱动地址匹配规则

    常用PLC与IFIX /的InTouch驱动地址匹配规则如下(持续更新): 1.施耐德M580<----->Intouch的/ IFIX: AI:400102<-----> 4 ...

  9. js问题记录

    1.aixos请求响应302重定向时无法获取返回数据, 解决方法:在请求头中添加  headers: { 'X-Requested-With': 'XMLHttpRequest' },

  10. SickOs1.2靶机

    仅供个人娱乐 靶机信息 靶机下载地址:https://www.vulnhub.com/entry/sickos-12,144/一.主机发现  arp-scan -l 二.端口扫描 1. masscan ...