CodeTon Round #2
A Two 0-1 sequences
第一个串后 \(m - 1\) 位必须和第二个串一样,前面的位只要有一个第二个串的第一个字符就行。
后来加的题,没代码。
B Luke is a foodie
直接顺着扫到第一个不存在合法的 \(v\) 的区间给答案 +1,然后从这里再开始做就行。
Code
#include <cstdio>
#include <iostream>
using namespace std;
int n, d;
void solve() {
cin >> n >> d;
int ans = 0, l = 0, r = 2e9;
while (n--) {
int x;
cin >> x;
int a = x - d, b = x + d;
if (b < l || a > r) {
ans++;
l = a, r = b;
} else
l = max(l, a), r = min(r, b);
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t;
cin >> t;
while (t--)
solve();
}
C Virus
把所有未被感染的连续段拉出来,一定是从长的开始,堵上两边,模拟就行。
Code
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
const int N = 100005;
int n, m, a[N], b[N];
void solve() {
cin >> n >> m;
for (int i = 1; i <= m; i++)
cin >> a[i];
sort(a + 1, a + m + 1);
b[1] = a[1] - 1 + n - a[m];
for (int i = 2; i <= m; i++)
b[i] = a[i] - a[i - 1] - 1;
sort(b + 1, b + m + 1, greater<int>());
int sum = 0;
for (int i = 1; i <= m; i++) {
b[i] -= 4 * (i - 1);
if (b[i] == 1)
sum += 1;
if (b[i] > 1)
sum += b[i] - 1;
}
cout << n - sum << '\n';
}
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t;
cin >> t;
while (t--)
solve();
}
D Magical Array
感觉挺妙的,俺痛 4min 就切了,感觉很牛逼。我想了很长时间(至少 20min),智商不太够。
发现对于 \(s = \sum\limits_{i = 1}^n ia_i\),操作一不会改变 \(s\),而操作二会使 \(s\) 加上 \(1\),把每个数组的 \(s\) 算一下就行。
Code
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
int n, m;
void solve() {
cin >> n >> m;
long long mn = 1ll << 60, mx = 0;
int p = 0;
for (int i = 1; i <= n; i++) {
long long s = 0;
for (int j = 1; j <= m; j++) {
long long x;
cin >> x;
s += x * j;
}
if (s < mn)
mn = s;
if (s > mx)
mx = s, p = i;
}
cout << p << ' ' << mx - mn << '\n';
}
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t;
cin >> t;
while (t--)
solve();
}
E Count Seconds
先跑遍拓扑,然后发现 \(n\) 轮之后如果一个点有,那么它能到达的所有点一定有。所以暴力模拟前 \(n\) 轮,然后算出最后到达汇点的值就行。
Code
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
const int N = 1005, mod = 998244353;
int n, m, a[N], in[N];
bool u[N];
vector<int> to[N];
int add(int x, int y) { return x + y < mod ? x + y : x + y - mod; }
bool check() {
for (int i = 1; i <= n; i++)
if (a[i] > 0)
return 1;
return 0;
}
void solve() {
static int q[N];
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> a[i];
while (m--) {
int x, y;
cin >> x >> y;
to[x].push_back(y);
in[y]++;
}
int t = 0;
while ((++t) <= n) {
for (int i = 1; i <= n; i++)
if (in[i] == 0 && u[i] == 0) {
u[i] = 1, q[t] = i;
for (int j : to[i])
in[j]--;
break;
}
}
for (int i = 1; i <= n; i++)
u[i] = 0;
int ans = 0;
while (ans <= n && check()) {
ans++;
for (int i = n; i; i--)
if (a[q[i]]) {
a[q[i]]--;
for (int j : to[q[i]])
a[j]++;
}
}
if (!check()) {
cout << ans << '\n';
return;
}
for (int i = 1; i <= n; i++) {
a[q[i]] %= mod;
for (int j : to[q[i]])
a[j] = add(a[j], a[q[i]]);
}
cout << add(ans, a[q[n]]) << '\n';
}
void clear() {
for (int i = 1; i <= n; i++)
to[i].clear();
}
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t;
cin >> t;
while (t--) {
solve();
clear();
}
}
F Colouring Game
也挺妙的,完全不会了。
可以发现如果颜色个数不相等,那么谁的多谁就赢,因为刚开始一定是双方将一个 RB 或 BR 删掉,然后没了之后再每次删掉自己的一个颜色,那一定是个数少的人先寄。
不然两个人个数相等,就是比每次删一个 RB 或 BR,看谁先不能操作,就只能删自己的一个,然后寄了。
把每个 RB 相间的段提出来,因为删的是两个不同颜色,所以双方操作集合相同,是个公平博弈。直接对所有长度算出所有 sg 就行。
暴力 sg 是 \(O(n^2)\) 的,但是打表之后能发现一个有个在三十左右的 \(len\),sg 在 \(3 \times len\) 及以后有长为 \(len\) 的循环节,所以打前面一些就行。
这份代码是寄的,因为没有对 \(3 \times len\) 取 \(\min\),但之前数据太水了过了,懒得改了。
Code
#include <cstdio>
#include <iostream>
using namespace std;
int n, f[103];
char a[500005];
void prep() {
for (int i = 2; i <= 102; i++) {
bool u[10] = {0};
for (int j = 1; j != i; j++)
u[f[j - 1] ^ f[i - j - 1]] = 1;
for (int x = 0;; x++)
if (!u[x]) {
f[i] = x;
break;
}
}
}
void solve() {
cin >> n >> (a + 1);
int s = 0, ans = 0;
for (int i = 1, j; i <= n; i = j + 1) {
for (j = i; j < n && a[j] != a[j + 1]; j++)
;
ans ^= f[j - i + 1];
}
for (int i = 1; i <= n; i++)
if (a[i] == 'R')
s++;
else
s--;
if (s > 0 || (s == 0 && ans))
cout << "Alice\n";
else
cout << "Bob\n";
}
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
prep();
int t;
cin >> t;
while (t--)
solve();
}
G Mio and Lucky Array
忽略一些细节,只讲大致思路。
将 \(a\) 和 \(b\) 的偶数位取反,于是操作变成根据操作开始位置的奇偶性给后面加或减 \(1, 2, \cdots\)。然后做二阶差分,枚举一个开始位置,就要求从这里之后 \(a\) 和 \(b\) 对位相差不超过一,且根据奇偶性是 \(a_i \le b_j \le a_i + 1\) 或 \(b_j \le a_i \le b_j + 1\)。这个是类似经典的 fft 做字符串匹配,可能要设计一个权值,不然平方的话值大于 \(998244353\) 可能在模意义下被判为合法。
还有差分之前的两位是要特殊判的,大概就是开始位置 \(i\) 前面操作的带符号和。\(i + 1\) 位和 \(i\) 的差告诉了 奇数位操作次数 - 偶数位操作次数,而 \(i\) 告诉了 奇数位操作位置和 - 偶数位操作位置和。由于操作是连续的,也就是说是 \(1, 2, 3, 4, \cdots, i\) 都可以操作,所以最后确定了操作次数的差,就可以贪心算出在这个差下 \(a_i\) 可能的最小值和最大值,而在这之间奇偶性相同应该都可以取到,判一下 \(a_i\) 是否在这个区间里即可。
这题是后来加的,所以口胡,没代码。
H Game of AI
建立一张有向图,\(i\) 指向 \(a_i\),那么就得到了一个基环树森林。考虑计算这个基环树森林的答案,那么对于环之外的每个点,如果它没有被孩子占领,那么它的孩子一定会它孩子的孩子占领。于是可以设 \(F_0(x), F_1(x)\) 分别表示自己被孩子占领/不被孩子占领的树的生成函数(EGF),那么则有:
\(F_0(x)\) 递推即为确定被哪个孩子占领,\(F_1(x)\) 就是孩子全部被孩子的孩子占领。
将 \((2)\) 带入 \((1)\) 即可得到 \(F_0(x)\) 的方程,牛顿迭代即可得到 \(F_0(x)\),进而得到 \(F_1(x)\)。
现在考虑把树拼起来变成一棵基环树。我们设在环上的一个点和它的子树,它被占领/不被占领的生成函数为 \(G_0(x), G_1(x)\),那么考虑一个点是被自己的孩子占领还是被环上的上一个点占领,生成函数就是 \(G_0(x) = F_0(x) + F_1(x)\)。
考虑环上每一个点,如果某个点没有被别人占领那么它在环上的后一个点一定会被占领。那么如果一个点它没有被别的点占领,我们就在后面强制拼上一个被占领的点,即 \(G_1(x) = F_1(x)G_0(x)\)。
对于环还有一个限制,就是不能环上的每一个点都被它在环上的前一个点占领。可以证明一个环合法当且仅当这两个条件成立。
设 \(G_2(x) = G_0(x) + G_1(x)\)。先不考虑第二个限制,那么一个环的生成函数就是:
减掉 \(G_0(x)\) 是因为环上不能只有一个点。
考虑第二个限制,类似上面可以得到违反第二条限制的生成函数是 \(-\ln(1 - F_1(x)) - F_1(x)\)。
两部分相减得到基环树的生成函数之后再 \(\exp\) 一下就能得到原问题(基环树森林)的答案了。
时间复杂度 \(O(n \log n)\)。
Code
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef unsigned long long u64;
typedef __uint128_t u128;
const int o = 18, len = 1 << o, B = 16;
int n, fac[len], ifac[len], iv[len], mod;
int f[len], ef[len], xef[len], a[len], b[len], h[len];
struct fastmod {
u64 b;
int m;
fastmod(int mod) : b(((u128)1 << 64) / mod), m(mod) {}
int reduce(u64 a) {
u64 q = ((u128)a * b) >> 64;
int r = a - q * m;
return r < m ? r : r - m;
}
} z(998244353);
int add(int x, int y) { return x + y < mod ? x + y : x + y - mod; }
int sub(int x, int y) { return x < y ? x + mod - y : x - y; }
int power(int a, int n) {
int tp = 1;
while (n) {
if (n & 1)
tp = z.reduce(1ll * tp * a);
a = z.reduce(1ll * a * a), n >>= 1;
}
return tp;
}
void prep(int n) {
fac[0] = 1;
for (int i = 1; i <= n; i++)
fac[i] = z.reduce(1ll * fac[i - 1] * i);
ifac[n] = power(fac[n], mod - 2);
for (int i = n - 1; i != -1; i--)
ifac[i] = z.reduce(1ll * ifac[i + 1] * (i + 1));
for (int i = 1; i <= n; i++)
iv[i] = z.reduce(1ll * ifac[i] * fac[i - 1]);
iv[0] = 1;
}
namespace poly {
int I[len], w[len], r[len], up, l;
int findg(int n) {
static int a[101];
int cnt = 0, x = n - 1;
for (int i = 2; i * i <= x; i++)
if (x % i == 0) {
a[++cnt] = i;
while (x % i == 0)
x /= i;
}
if (x > 1)
a[++cnt] = x;
for (int g = 2;; g++) {
bool ok = 1;
for (int i = 1; i <= cnt; i++)
if (power(g, (n - 1) / a[i]) == 1) {
ok = 0;
break;
}
if (ok)
return g;
}
}
void init() {
I[0] = 1;
const int w0 = power(findg(mod), (mod - 1) >> o);
w[len >> 1] = 1;
for (int i = (len >> 1) + 1; i != len; i++)
w[i] = z.reduce(1ll * w[i - 1] * w0);
for (int i = (len >> 1) - 1; i; i--)
w[i] = w[i << 1];
for (int i = 0; i != len; i++)
r[i] = (r[i >> 1] >> 1) | ((i & 1) << (o - 1));
}
void ntt(int *a, int n, bool op) {
static u64 t[len], x, y;
for (int i = 0; i != n; i += 2) {
x = a[r[i] >> (o - l)], y = a[r[i + 1] >> (o - l)];
t[i] = x + y, t[i + 1] = x + mod - y;
}
for (int l = 2; l != n; l <<= 1) {
int *k = w + l;
for (u64 *f = t; f != t + n; f += l)
for (int *j = k; j != k + l; j++, f++) {
u64 x = *f, y = z.reduce(f[l] * *j);
f[l] = x + mod - y, *f += y;
}
}
if (op) {
for (int i = 0, x = mod - (mod >> l); i != n; i++)
a[i] = z.reduce(t[i] * x);
reverse(a + 1, a + n);
} else
for (int i = 0; i != n; i++)
a[i] = z.reduce(t[i]);
}
void pre(int n) { l = 32 - __builtin_clz(n), up = 1 << l; }
void mul(int *f, int n, int *g, int m, int *h, int q) {
static int x[len], y[len];
memcpy(x, f, (n + 1) << 2), memcpy(y, g, (m + 1) << 2);
pre(n + m), ntt(x, up, 0), ntt(y, up, 0);
for (int i = 0; i < up; i++)
h[i] = z.reduce(1ll * x[i] * y[i]);
ntt(h, up, 1);
memset(x, 0, up << 2), memset(y, 0, up << 2), fill(h + q + 1, h + up, 0);
}
void div(int *a, int *b, int n, int *f) {
static int iv[len], x[len], tmp[len];
static int nb[len << 2], nf[len << 2];
static u64 s0[len], s1[len];
if (n <= 16) {
int x = power(b[0], mod - 2);
for (int i = 0; i <= n; i++) {
u64 s = 0;
for (int j = 0; j != i; j++)
s += 1ll * f[j] * b[i - j];
f[i] = z.reduce(1ll * x * (a[i] + mod - z.reduce(s)));
}
return;
}
int m = 1 << (32 - __builtin_clz(n));
int k = m >> 4, z = k << 1;
div(I, b, k - 1, tmp), memcpy(x, a, k << 2);
memcpy(iv, tmp, k << 2), memset(tmp, 0, k << 2);
pre(z - 1);
ntt(iv, up, 0), ntt(x, up, 0);
for (int i = 0; i != up; i++)
x[i] = ::z.reduce(1ll * x[i] * iv[i]);
ntt(x, up, 1);
memcpy(f, x, k << 2);
memset(x, 0, up << 2);
memcpy(nb, b, k << 2);
ntt(nb, up, 0);
for (int i = 1;; i++) {
if (i * k > n) {
memset(iv, 0, up << 2);
memset(nb, 0, i * z * 4);
memset(nf, 0, (i - 1) * z * 4);
fill(f + n + 1, f + i * k, 0);
break;
}
memcpy(nb + i * z, b + i * k, k << 2);
memcpy(nf + (i - 1) * z, f + (i - 1) * k, k << 2);
ntt(nb + i * z, up, 0), ntt(nf + (i - 1) * z, up, 0);
for (int l1 = 0; l1 != i; l1++)
for (int j = 0; j != up; j++)
s0[j] += 1ll * nf[l1 * z + j] * nb[(i - l1) * z + j];
for (int l1 = 0; l1 != i; l1++)
for (int j = 0; j != up; j++)
s1[j] += 1ll * nf[l1 * z + j] * nb[(i - l1 - 1) * z + j];
for (int j = 0; j != up; j += 2) {
x[j] = ::z.reduce(s0[j] + ::z.reduce(s1[j]));
x[j + 1] = ::z.reduce(s0[j + 1] + mod - ::z.reduce(s1[j + 1]));
s0[j] = s1[j] = s0[j + 1] = s1[j + 1] = 0;
}
ntt(x, up, 1);
memset(x + k, 0, k << 2);
for (int j = 0; j != k; j++)
x[j] = sub(a[i * k + j], x[j]);
ntt(x, up, 0);
for (int j = 0; j != up; j++)
x[j] = ::z.reduce(1ll * x[j] * iv[j]);
ntt(x, up, 1);
memcpy(f + i * k, x, k << 2);
memset(x, 0, up << 2);
}
}
void dcexp(int *a, int l, int r, int n, int *f, int *g, int *h) {
static u64 s[len];
static int tp[len];
if (r - l + 1 <= 32) {
for (int i = l; i <= r && i <= n; i++) {
u64 s = 0;
for (int j = l; j < i; j++) {
s += 1ll * f[j] * a[i - j];
if (!(j & 15))
s = z.reduce(s);
}
f[i] = z.reduce(((u64)f[i] + z.reduce(s)) * iv[i]);
}
return;
}
int *tg[B], *th[B];
int len = (r - l + 1) / B, k = 2 * len;
for (int i = 0; i < B - 1; i++)
tg[i] = g + i * k, th[i] = h + i * k;
if (!l) {
pre(k - 1);
for (int i = 0; i < B - 1; i++) {
if ((i + 1) * len > n)
break;
memcpy(th[i], a + i * len, k << 2);
ntt(th[i], k, 0);
}
}
for (int i = 0; i < B; i++) {
if (l + i * len > n)
break;
memset(s, 0, k << 3);
for (int j = 0; j != i; j++)
for (int t = 0; t != k; t++)
s[t] += 1ll * tg[j][t] * th[i - j - 1][t];
for (int t = 0; t != k; t++)
tp[t] = z.reduce(s[t]);
pre(k - 1), ntt(tp, k, 1);
for (int t = 0; t < len; t++)
f[l + i * len + t] = add(f[l + i * len + t], tp[t + len]);
dcexp(a, l + i * len, l + (i + 1) * len - 1, n, f, g + k * B, h + k * B);
if (i != B - 1) {
memcpy(tg[i], f + l + i * len, len << 2);
pre(k - 1), ntt(tg[i], k, 0);
}
}
memset(tg[0], 0, (k * B) << 2);
}
void exp(int *a, int n, int *f) {
static int x[len << 1], v1[len << 2], v2[len << 2];
for (int i = 1; i <= n; i++)
x[i] = z.reduce(1ll * a[i] * i);
f[0] = 1, fill(f + 1, f + n + 1, 0);
int m = 1 << (32 - __builtin_clz(n));
dcexp(x, 0, m - 1, n, f, v1, v2);
memset(x, 0, (n + 1) << 2), fill(f + n + 1, f + m, 0);
}
} // namespace poly
void calc(int n) {
static int xef[len], eef[len], a[len], b[len], c[len];
if (n == 2) {
f[2] = 1;
return;
}
calc((n + 1) / 2);
poly::exp(f, n - 1, xef);
for (int i = n; i; i--)
xef[i] = xef[i - 1];
xef[0] = 0;
for (int i = 1; i <= n; i++)
a[i] = add(xef[i], f[i]);
poly::exp(a, n - 1, eef);
for (int i = n; i; i--)
eef[i] = eef[i - 1];
eef[0] = 0;
poly::pre(2 * n);
int up = poly::up;
poly::ntt(a, up, 0), poly::ntt(eef, up, 0);
for (int i = 0; i != up; i++)
c[i] = z.reduce(1ll * a[i] * eef[i]);
poly::ntt(c, up, 1), fill(c + n + 1, c + up, 0);
for (int i = 1; i <= n; i++)
c[i] = sub(f[i], c[i]);
xef[0] = 1;
poly::ntt(xef, up, 0);
for (int i = 0; i != up; i++)
b[i] = z.reduce(1ll * xef[i] * (a[i] + 1));
poly::ntt(b, up, 1);
fill(b + n + 1, b + up, 0);
poly::ntt(b, up, 0);
for (int i = 0; i != up; i++)
b[i] = z.reduce(1ll * b[i] * eef[i]);
poly::ntt(b, up, 1);
fill(b + n + 1, b + up, 0);
b[0] = 1;
for (int i = 1; i <= n; i++)
b[i] = sub(0, b[i]);
memset(a, 0, up << 2);
poly::div(c, b, n, a);
for (int i = 2; i <= n; i++)
f[i] = sub(f[i], a[i]);
memset(xef, 0, up << 2), memset(eef, 0, up << 2);
memset(a, 0, (n + 1) << 2), memset(b, 0, (n + 1) << 2), memset(c, 0, (n + 1) << 2);
}
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> mod, z = fastmod(mod);
if (n == 1) {
cout << 0;
return 0;
}
poly::init(), prep(n);
calc(n);
poly::exp(f, n, ef);
for (int i = n; i; i--)
a[i] = add(f[i], ef[i - 1]);
poly::exp(a, n, h);
for (int i = n; i; i--)
h[i] = h[i - 1];
h[0] = 0;
for (int i = 1; i <= n; i++)
a[i] = add(f[i], h[i]);
for (int i = n; i; i--)
xef[i] = ef[i - 1];
xef[0] = 1;
poly::mul(a, n, xef, n, a, n);
a[0] = 1;
for (int i = 1; i <= n; i++)
a[i] = sub(0, a[i]);
poly::mul(a, n, ef, n, a, n);
h[0] = 1;
for (int i = 1; i <= n; i++)
h[i] = sub(0, h[i]);
poly::div(h, a, n, b);
for (int i = 1; i <= n; i++)
cout << z.reduce(1ll * b[i] * fac[i]) << '\n';
}