「CF1930E」2..3...4.... Wonderful! Wonderful!

Description

Link:CF1930E

有一个长度为 nn 的数组 aa,初始满足 ai=ia_i = i。对于一个参数 kk (1kn121 \leq k \leq \lfloor \frac{n - 1}{2} \rfloor),你可以对 aa 进行任意次数(可能为 00 次)的以下操作:

  • aa 中选择一个长度为 2k+12k + 1 的子序列,然后删除其中的前 kk 个元素和后 kk 个元素。

求对于每个 kk (1kn121 \leq k \leq \lfloor \frac{n - 1}{2} \rfloor),最终可以得到多少个不同的数组 aa

数据范围:3n1063 \leq n \leq 10^6

时空限制:33s / 256256MiB

Solution

对于一种可能的局面,我们将已经被删除的元素标记成 11,没有被删除的元素标记成 00。那么有一个简单的必要条件:

  • 11 的个数是 2k2k 的倍数。
  • 至少存在一个 00,使得其左边和右边各自至少有 kk11

然后发现这也是充分的:先找到一个 00 作为操作中心,满足前后各自至少有 kk11。将其视为最后一次操作,那么考虑退回这次操作。先将这个 00 前后的 k1k - 111 还原成 00,此时不妨设左边 11 的数量大于右边 11 的数量,将右边第 kk11 还原成 00,此时全局应有奇数个 11选取最中间的那个 11 还原成 00(这个 11 位于左侧,符合要求),然后将这个 00 作为倒二次的操作中心,依此类推。

考虑计数,假设选的参数为 kk,一共删了 xx 个数(xxkk 的倍数)。正难则反,考虑使用总方案数减去不合法的方案数。

可以先将这 xx11 摆好,然后将剩下的 00 插入进去。要想使得其不合法,00 只能插在前 kk11 的左侧或后 kk11 的右侧,插在中间的空隙是不行的。将 nxn - x00 分成 2k2k 个组是一个简单的插板法,于是贡献为

(nx)(nx+2k12k1)\binom{n}{x} - \binom{n - x + 2k - 1}{2k - 1}

时间复杂度 O(nlogn)\mathcal{O}(n \log 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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#include <bits/stdc++.h>

using i64 = long long;

#define debug(a) std::cout << #a << '=' << (a) << ' '

template <class T>
inline void chmin(T &x, const T &y) {
if (x > y) {
x = y;
}
}
template <class T>
inline void chmax(T &x, const T &y) {
if (x < y) {
x = y;
}
}

const int mod = 998244353;

inline void add(int &x, const int &y) {
x += y; if (x >= mod) x -= mod;
}
inline void dec(int &x, const int &y) {
x -= y; if (x < 0) x += mod;
}

template <class T>
constexpr int qpow(int a, T b, int p) {
int ans = 1;
for (; b; b >>= 1) {
if (b & 1) ans = 1ll * ans * a % p;
a = 1ll * a * a % p;
}
return ans;
}

const int N = 1000100;

int n;

struct BinomCoef {
std::vector<int> fact, facv;

BinomCoef() {}
BinomCoef(int n) {
init(n);
}

void init(const int &n) {
fact.resize(n + 1), facv.resize(n + 1);

fact[0] = 1;
for (int i = 1; i <= n; i ++) {
fact[i] = 1ll * fact[i - 1] * i % mod;
}
facv[n] = qpow(fact[n], mod - 2, mod);
for (int i = n - 1; i >= 0; i --) {
facv[i] = 1ll * facv[i + 1] * (i + 1) % mod;
}
}

int binom(int n, int m) {
if (n < m || m < 0) {
return 0;
}
return 1ll * facv[m] * facv[n - m] % mod * fact[n] % mod;
}
};

void work() {
std::cin >> n;

BinomCoef bc(n * 2);

for (int k = 1; k <= (n - 1) / 2; k ++) {
int ans = 1;
for (int x = 2 * k; x < n; x += 2 * k) {
add(ans, bc.binom(n, x));
dec(ans, bc.binom(n - x + 2 * k - 1, 2 * k - 1));
}
std::cout << ans << ' ';
}
std::cout << '\n';
}

int main() {
std::ios::sync_with_stdio(0);
std::cin.tie(0);

int T;
std::cin >> T;

while (T --) {
work();
}

return 0;
}