2022.10.18 模拟赛题解

比赛链接

A. 菜

原题链接

这种题赛时能写假做法……有猪啊!

考虑维护一个栈,每次加入一颗菜,然后尝试和当前栈顶的菜合并。正确性显然。

其中如果暴力求 \(\operatorname{lcm}\) 会炸,所以考虑用 bitset 维护每个因子是否出现。

时间复杂度 \(O(\frac{nV}{\omega})\)\(V\) 是值域。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <cstdio>
#include <algorithm>
#include <bitset>
#include <vector>

const int N = 1e5 + 5;

int n;
std::bitset<1000> a[N];

std::vector<std::bitset<1000>> stk;

int gcd(int x, int y) { return y == 0 ? x : gcd(y, x % y); }
int lcm(int x, int y) { return x / gcd(x, y) * y; }

int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
int x;
scanf("%d", &x);
for(int j = 2; j <= 700; j++) if(x % j == 0) a[i][j] = 1;
}
for(int i = 1; i <= n; i++) {
stk.push_back(a[i]);
while(stk.size() >= 2 && (stk.back() & stk.end()[-2]).any()) {
auto x = stk.back(); stk.pop_back();
auto y = stk.back(); stk.pop_back();
stk.push_back(x | y);
}
}
puts(stk.size() > 1 ? "No" : "Yes");
return 0;
}

B. 狗

原题链接

发现 U/DL/R 互不影响,所以拆成 \(2\cdot n\) 个括号序列。

容易发现每个括号序列互不影响,所以对于每个序列统计方案数和答案(所有方案中匹配上的括号数的和)然后直接算就好了。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <cstdio>
#include <algorithm>
#include <vector>

void read(int &x) {
char ch = getchar();
while(ch < '0' || ch > '9') ch = getchar();
for(x = 0; '0' <= ch && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
}

typedef long long LL;

const int N = 500 + 5;
const int MOD = 998244353;

int n;
int a[N][N];
char b[N][N];

std::vector<int> v[2 * N], w[2 * N];
int f[N][N], g[N][N], fv[2 * N], gv[2 * N];

void mod(int &x) { x >= MOD ? x -= MOD : 0; }

int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
char ch = getchar();
while(ch < 'A' || ch > 'Z') ch = getchar();
for(int j = 1; j <= n; j++) b[i][j] = ch, ch = getchar();
}
for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) read(a[i][j]);
for(int i = 1; i <= 2 * n; i++) v[i].push_back(0), w[i].push_back(0);
for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++)
if(b[i][j] == 'L' || b[i][j] == 'R') v[i].push_back(b[i][j] == 'R' ? 1 : -1), w[i].push_back(a[i][j]);
for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++)
if(b[j][i] == 'U' || b[j][i] == 'D') v[i + n].push_back(b[j][i] == 'D' ? 1 : -1), w[i + n].push_back(a[j][i]);
for(int k = 1; k <= 2 * n; k++) {
int sz = (int)v[k].size() - 1;
for(int i = 0; i <= sz; i++) for(int j = 0; j <= sz; j++) f[i][j] = 0, g[i][j] = -1;
f[0][0] = 1, g[0][0] = 0;
for(int i = 1; i <= sz; i++) for(int j = 0; j <= i; j++) {
mod(f[i][j] += f[i - 1][j]);
if(g[i - 1][j] >= 0) g[i][j] = g[i - 1][j];
if(0 <= j - v[k][i] && j - v[k][i] <= sz) {
LL extra = (v[k][i] == -1 ? j - v[k][i] : 1);
mod(f[i][j] += f[i - 1][j - v[k][i]] * extra % MOD);
if(g[i - 1][j - v[k][i]] >= 0) {
if(g[i][j] == -1) g[i][j] = 0;
mod(g[i][j] += (g[i - 1][j - v[k][i]] + (LL)f[i - 1][j - v[k][i]] * w[k][i] % MOD) * extra % MOD);
}
}
}
fv[k] = f[sz][0], gv[k] = g[sz][0];
// for(int i = 1; i <= sz; i++) for(int j = 0; j <= sz; j++) printf("g[%d][%d] = %lld\n", i, j, g[i][j]);
// printf("%d: (f=%lld, g=%lld) ", k, fv[k], gv[k]);
// for(int i = 1; i <= sz; i++) printf("%d(%d) ", v[k][i], w[k][i]);
// puts("");
}
int ans = 0;
for(int i = 1; i <= 2 * n; i++) {
LL ret = 1;
for(int j = 1; j <= 2 * n; j++) if(i != j) (ret *= fv[j]) %= MOD;
(ans += ret * gv[i] % MOD) %= MOD;
}
printf("%d\n", ans);
return 0;
}

C. 可

原题链接

做法 1(80 分)

首先显然题目就是求所有方案的 \(f\) 值的和。

如果 \(k=1\) 那么显然是数位 DP。

考虑如何拓展到 \(k>1\)。设 \(dp(i,j)\) 表示前 \(i\) 位,有 \(j\) 个卡上界,转移时需要再记一个 DP。总复杂度是 \(O(nk^4)\)

做法 2(100 分)

\(g(s)\) 表示 \(\sum a_i = s\) 的方案数,那么就有一个经典的容斥:\(g(s) = \sum_{i=0}^i (-1)^i \binom{k}{i}\binom{s - i(x + 1) + k - 1}{k - 1}\),其中 \(s - k(x + 1) + k - 1 < k - 1\)\(\binom{s - k(x + 1) + k - 1}{k - 1}\) 定义为 \(0\)

那么答案就是 \(\sum_{i=0}^kx f(i)g(i)\)

推一下式子:

\[ \begin{aligned} &\sum_{i=0}^{kx} f(i)g(i)\\ =&\sum_{i=0}^{kx} f(i) \sum_{j=0}^k (-1)^j \binom{k}{j} \binom{i - j(x + 1) + k - 1}{k - 1}\\ =&\sum_{j=0}^k (-1)^j \binom{k}{j} \sum_{i=0}^{kx} f(i) \binom{i - j(x + 1) + k - 1}{k - 1} \end{aligned} \]

然后就很妙了。我们发现 \(\binom{i - j(x + 1) + k - 1}{k - 1} = \dfrac{\prod_{r = i - j(x + 1) + 1}^{i - j(x + 1) + k - 1} r}{(k - 1)!} = \prod_{r = 1}^{k - 1}\left(\dfrac{1}{(k - 1)!}i + \dfrac{-j(x + 1) + r}{(k - 1)!}\right)\),这是一个关于 \(i\)\(k - 1\) 次多项式。由于 \(k\) 很小,所以可以暴力乘出系数。我们设 \(l\) 次项的系数为 \(c(j, l)\)。但是上面我们说过当 \(s - k(x + 1) + k - 1 < k - 1\)\(\binom{s - k(x + 1) + k - 1}{k - 1}\) 定义为 \(0\),然而这里拆成多项式后就不对了。所以我们考虑将 \(\sum_{i=0}^{kx} f(i) \binom{i - j(x + 1) + k - 1}{k - 1}\) 中的 \(\sum_{i=0}^{kx}\) 改成 \(\sum_{i=j(x + 1)}^{kx}\)

那么上式就等于

\[ \sum_{j=0}^k (-1)^j \binom{k}{j} \sum_{l=0}^{k - 1} c(j, l) \sum_{i=j(x + 1)}^{kx} f(i) i^l \]

所以现在的问题就转化成了求 \(\sum_{i=0}^{s} f(i)i^l\)(然后用 \(\left(\sum_{i=0}^{kx} f(i)i^l\right) - \left(\sum_{i=0}^{j(x + 1) - 1} f(i)i^l\right)\) 就好了)。

我们设 \(dp(i, j)\) 表示 \(\sum\limits_{t=0}^{s \bmod 10^i} f(t)t^j\)。然后我们来推式子:(没写范围的变量和上一行范围一样)

\[ \begin{aligned} &dp(i, j)\\ =&\sum_{y=0}^{s \bmod 10^i} f(y)y^j\\ =&\sum_{\substack{y\in [0, s \bmod 10^i]\\t = y \bmod 10^{i - 1}\\t' = y - t}} f(t + t') (t + t')^j\\ =&\sum_{t'} \sum_{t} f(t)f(t') \sum_{r=0}^j \binom{j}{r} t^r (t')^{j - r}\\ =&\sum_{t'} f(t') \sum_{r=0}^j \binom{j}{r} (t')^{j - r} \sum_{t} f(t)t^r\\ =&\sum_{r=0}^j \sum_{t'} f(t') \binom{j}{r} (t')^{j - r} dp(i - 1, r) \end{aligned} \]

所以单次 DP 的复杂度是 \(O(\log kx \cdot k^2) = O(k^2(\log k + \log x)) = O(k^2 \log x)\),还带一个 \(10\) 的常数。

由于要跑 \(O(k)\) 次 DP,所以总复杂度是 \(O(k^3 \log x)\) 的。所以整道题的总复杂度也是 \(O(k^3\log x)\) 的。

是不是这样就过了呢?我们发现常熟太大,炸了。考虑优化掉 \(10\) 的常数:

\[ \begin{aligned} &\sum_{r=0}^j \sum_{t'} f(t') \binom{j}{r} (t')^{j - r} dp(i - 1, r)\\ =&\sum_{r=0}^j \left(\sum_{t'}f(t')(t')^{j-r}\right)\binom{j}{r} dp(i - 1, r) \end{aligned} \]

考虑预处理 \(sum(u, q) = \sum_{t'=0}^u f(t')(t')^q\),就可以优化掉 \(10\) 的常数了。

最终复杂度 \(O(k^3 \log x)\)

代码

No code yet.

D. 爱

No solution yet.