Educational Codeforces Round 142 (A - F2)

赛时:A B C D
补题:A B C D E F1 F2
Rank: 1199

A

第一种操作只有对两个 1 作用才会更优。

Copy
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <map> #include <set> #include <cstdlib> #include <cmath> #include <deque> using namespace std; #define pii pair<int,int> #define mp make_pair #define fi first #define se second const int N = 5e5 + 10; int read() { int x = 0, f = 1; char c = getchar(); while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); } while (c >= '0' && c <= '9') { x = (x << 1) + (x << 3) + (c ^ 48); c = getchar(); } return x * f; } int T, n; int a[N]; int main() { T = read(); while (T--) { n = read(); for (int i = 1; i <= n; i++) a[i] = read(); sort(a + 1, a + n + 1); int cnt = 0; for (int i = 1; i <= n; i++) { if (a[i] > 1) break; cnt++; } cnt = n - cnt + (cnt / 2) + (cnt % 2); printf("%d\n", cnt); } return 0; }

B

首先用光所有 a1,然后交替使用 a2,a3,最后使用 a4

Copy
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <map> #include <set> #include <cstdlib> #include <cmath> #include <deque> using namespace std; #define pii pair<int,int> #define mp make_pair #define fi first #define se second const int N = 5e5 + 10; int read() { int x = 0, f = 1; char c = getchar(); while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); } while (c >= '0' && c <= '9') { x = (x << 1) + (x << 3) + (c ^ 48); c = getchar(); } return x * f; } int T, a, b, c, d, A, B, ans; int main() { T = read(); while (T--) { a = read(), b = read(), c = read(), d = read(); ans = a; A = a, B = a; if (a == 0) { puts("1"); continue; } if (b >= c) { ans += c * 2; b -= c; if (B < b) ans += B + 1; else { B -= b; ans += b; ans += min(d, min(A, B) + 1); } } else { ans += b * 2; c -= b; if (A < c) ans += A + 1; else { A -= c; ans += c; ans += min(d, min(A, B) + 1); } } printf("%d\n", ans); } return 0; }

C

posi 为满足 pposi=i 的位置。
我们将 i[1,n2]ni+1 一起考虑,因为最优策略下,一定是 ini+1 操作。
首先,如果 i 操作,那么 j[1,i) 也得操作。
其次,如果 posi>posni+1,那么 i 需要操作。
最后,如果对于 i[1,n2]posi<posi1 或者 posni+1>posni+2,那么 i1 需要操作。
将所有需要操作的数取最大值,就是答案。

Copy
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <map> #include <set> #include <cstdlib> #include <cmath> #include <deque> using namespace std; #define pii pair<int,int> #define mp make_pair #define fi first #define se second const int N = 5e5 + 10; int read() { int x = 0, f = 1; char c = getchar(); while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); } while (c >= '0' && c <= '9') { x = (x << 1) + (x << 3) + (c ^ 48); c = getchar(); } return x * f; } int T, n, ans; int p[N], pos[N]; int main() { T = read(); while (T--) { n = read(); for (int i = 1; i <= n; i++) p[i] = read(), pos[p[i]] = i; ans = 0; if (n == 1) {puts("0"); continue;} if (n == 2) { if (pos[1] == 1) puts("0"); else puts("1"); continue; } int cur = 0; for (int i = 1; i <= n / 2; i++) if (pos[i] > pos[n - i + 1]) cur = i; for (int i = 1; i <= (n + 1) / 2; i++) if (pos[i] < pos[i - 1] || pos[n - i + 1] > pos[n - i + 2]) cur = max(cur, i - 1); printf("%d\n", cur); } return 0; }

D

对于一个 p 序列,我们要求出是否有 q 序列满足前 k 位都有 qpi=i,相当于要求 q 的第 pi 位为 i
这是非常困难的,于是我们考虑反过来算。
已知一个 q 序列,它对哪些 p 如何贡献。
posi 满足 qposi=i,那么相当于要求 pi=posi
于是我们枚举 j 属于 1m,然后让前缀等于 pos1,pos2,...,posjp 的序列的答案对 jmax
简单哈希加 map 即可。

Copy
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <map> #include <set> #include <cstdlib> #include <cmath> #include <deque> using namespace std; #define pii pair<int,int> #define mp make_pair #define fi first #define se second const int N = 5e4 + 10, M = 15; typedef long long ll; int read() { int x = 0, f = 1; char c = getchar(); while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); } while (c >= '0' && c <= '9') { x = (x << 1) + (x << 3) + (c ^ 48); c = getchar(); } return x * f; } int T, n, m; int a[N][M], p[M], pos[M]; map<ll, int> vis[M]; map<ll, int> ans[M]; ll count(int len) { ll res = 0; for (int i = 1; i <= len; i++) res = (res * 10) + (ll)p[i] - 1ll; return res; } int main() { T = read(); while (T--) { n = read(), m = read(); for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) a[i][j] = read(), p[j] = a[i][j]; } for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) pos[a[i][j]] = j; for (int res = 1; res <= m; res++) { for (int j = 1; j <= res; j++) p[j] = pos[j]; ans[res][count(res)]++; } } for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) p[j] = a[i][j]; int res = 0; for (int j = 1; j <= m; j++) if (ans[j][count(j)]) res = j; printf("%d ", res); } puts(""); for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) pos[a[i][j]] = j; for (int res = 1; res <= m; res++) { for (int j = 1; j <= res; j++) p[j] = pos[j]; ans[res][count(res)] = 0; } } } return 0; }

E

结论:1018 的数最多有 105 个因子,109 的数最多有 103 个因子。
因此我们首先暴力枚举出 m1,m2 的所有因子,然后直接做笛卡尔积求出 106 个可能的因子。
排序后去重,接下来考虑对于某一个因子如何算出它最早在第几行出现。
这里有一个复杂度奇奇怪怪的神秘做法。
对于每一个因子 i,它出现的行数一定是它自己的因子,因此也一定是 m 的因子。
于是我们二分找到最小的满足 didjnj,然后从 j 开始向上枚举,直到 djdi 的因子或 dj>n

Copy
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <map> #include <set> #include <cstdlib> #include <cmath> #include <deque> using namespace std; #define pii pair<int,int> #define mp make_pair #define fi first #define se second const int N = 2e6 + 10; #define int long long int read() { int x = 0, f = 1; char c = getchar(); while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); } while (c >= '0' && c <= '9') { x = (x << 1) + (x << 3) + (c ^ 48); c = getchar(); } return x * f; } int T, n, m1, m2, cnt1, cnt2, cnt3, ans, cnt; int a[N], b[N], c[N]; signed main() { T = read(); while (T--) { n = read(), m1 = read(), m2 = read(); cnt1 = cnt2 = cnt3 = 0; ans = cnt = 0; for (int i = 1; i * i <= m1; i++) { if (m1 % i) continue; a[++cnt1] = i; if (i * i != m1) a[++cnt1] = m1 / i; } for (int i = 1; i * i <= m2; i++) { if (m2 % i) continue; b[++cnt2] = i; if (i * i != m2) b[++cnt2] = m2 / i; } sort(a + 1, a + cnt1 + 1); sort(b + 1, b + cnt2 + 1); for (int i = 1; i <= cnt1; i++) for (int j = 1; j <= cnt2; j++) c[++cnt3] = a[i] * b[j]; sort(c + 1, c + cnt3 + 1); cnt3 = unique(c + 1, c + cnt3 + 1) - c - 1; for (int i = 1; i <= cnt3; i++) { int l = 1, r = i, mid, x = 0; while (l <= r) { mid = (l + r) >> 1; if ((c[i] + c[mid] - 1) / c[mid] <= n) r = mid - 1, x = mid; else l = mid + 1; } int flag = 0; for (int j = x; c[j] <= n; j++) { if (c[i] % c[j] == 0) { flag = 1; ans ^= c[j]; cnt++; break; } } } printf("%lld %lld\n", cnt, ans); } return 0; }

F1

结论:一个子点集不可能既不蓝联通也不红联通。
证明:
因为整个图为完全图,所以一个子点集及其相关的边也是完全图。
容易发现,保留所有蓝边和保留所有红边互为补图,接下来只需要证明一个完全图自己或者其补图至少有一个联通。
假设在原图中有两个之间没有边的联通块。
那么在补图中这两个联通块之间的所有点对都有连边,也就一定联通。
有了这个结论,我们只需要计算不存在同时蓝联通和红联通的染色方案即可。
显然整个图作为一个子图也是蓝联通/红联通的。
fi 表示 i 个点,整个图蓝联通的方案数,gi 表示 i 个点,整个图蓝联通/红联通的方案数。
如何转移?
枚举 1 号点所在的极大蓝联通块大小 j,那么这种方案对 fi 的贡献是 fjgij(i1j1)
因为这个联通块是极大的,所以这 j 个点到其余 ij 的所有边都必须是红色的,因此此时算出的是对整个图红联通方案数的贡献。
把红色和蓝色互换后所有子图的联通颜色也互换,所以整个图红联通的方案数等于整个图蓝联通的方案数,这样贡献的是对的。
于是有转移方程:fi=j=1i1fjgij(i1j1)gi=2fi
容易发现第二条对 i=1 不成立,f1,g1 特判即可。
答案是 gn2

Copy
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <map> #include <set> #include <cstdlib> #include <cmath> #include <deque> using namespace std; #define pii pair<int,int> #define mp make_pair #define fi first #define se second const int N = 5e3 + 10, mod = 998244353; #define int long long int read() { int x = 0, f = 1; char c = getchar(); while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); } while (c >= '0' && c <= '9') { x = (x << 1) + (x << 3) + (c ^ 48); c = getchar(); } return x * f; } int n; int C[N][N], f[N], g[N]; signed main() { n = read(); C[0][0] = 1; for (int i = 1; i <= n; i++) { C[i][0] = 1; for (int j = 1; j <= i; j++) C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod; } f[1] = 1, g[1] = 1; for (int i = 2; i <= n; i++) { for (int j = 1; j < i; j++) f[i] = (f[i] + f[j] * g[i - j] % mod * C[i - 1][j - 1] % mod) % mod; g[i] = (f[i] * 2) % mod; } printf("%lld\n", (g[n] + mod - 2) % mod); return 0; }

F2

F1 中的式子拆开:Bi=j=1i1BjAij(i1j1)。这里的 Bi 对应 fiAi 对应 gi
Bi=j=0iBjAij(i1)!(ij)!(j1)!=(i1)!j=0iBj(j1)!Aij(ij)!
Ci=Aii!,Di=Bi(i1)!
Bi=(i1)!j=0iCijDj
这是一个非常漂亮的卷积形式,可惜 CiDi 都对 Bi 有依赖性。
考虑根号重构,每 B 个元素计算一次 CD 的卷积。
假设当前计算到 i,上一次卷积的编号在 t,考虑如何利用卷积结果快速计算 Bi
容易发现,对于 ijt,jt 的所有 jCijDj 的和正好在卷积中计算过,答案记在 CD 卷积得到的 c 数组第 i 项。
换言之,ci=j=ittCijDj
距离 Bi 中的求和式只差 j<itj>t 的部分。
容易发现,这两部分的 j 最多只有 B 个,于是我们暴力计算这一部分的贡献即可。
Bi=(i1)!(ci+j=0it1CijDj+j=t+1iCijDj)
复杂度为 O(n2lognB+nB)
B=nlogn 时最优,为 O(nnlogn)

Copy
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <map> #include <set> #include <cstdlib> #include <cmath> #include <deque> using namespace std; #define pii pair<int,int> #define mp make_pair #define fi first #define se second const int N = 2e5 + 10, G = 3, GI = 332748118, mod = 998244353, T = 1000; #define int long long int read() { int x = 0, f = 1; char c = getchar(); while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); } while (c >= '0' && c <= '9') { x = (x << 1) + (x << 3) + (c ^ 48); c = getchar(); } return x * f; } int n, t; int qpow(int a, int b) { int res = 1; while (b) { if (b & 1) res = (res * a) % mod; a = (a * a) % mod; b >>= 1; } return res; } void NTT(int len, int * a, int coe) { for (int mid = 1; mid < len; mid <<= 1) { int Wn = qpow(((coe == 1) ? G : GI), (mod - 1) / (mid << 1)); for (int i = 0; i < len; i += mid << 1) { int W = 1; for (int j = 0; j < mid; j++, W = (W * Wn) % mod) { int x = a[i + j], y = (a[i + j + mid] * W) % mod; a[i + j] = (x + y) % mod; a[i + j + mid] = (x - y + mod) % mod; } } } } int rev[N], c[N]; void mul(int len, int * a, int * b) { for (int i = 1; i < len; i++) rev[i] = (rev[i >> 1] >> 1) + ((i & 1) ? (len >> 1) : 0); for (int i = 0; i < len; i++) if (i < rev[i]) swap(a[i], a[rev[i]]); NTT(len, a, 1); for (int i = 0; i < len; i++) if (i < rev[i]) swap(b[i], b[rev[i]]); NTT(len, b, 1); for (int i = 0; i <= len; i++) a[i] = (a[i] * b[i]) % mod; for (int i = 0; i < len; i++) if (i < rev[i]) swap(a[i], a[rev[i]]); NTT(len, a, -1); int inv = qpow(len, mod - 2); for (int i = 0; i <= len; i++) c[i] = (a[i] * inv) % mod; } int len; int A[N], B[N], C[N], D[N], a[N], b[N], inv[N], fac[N]; void init() { inv[0] = inv[1] = fac[0] = 1; for (int i = 1; i <= n; i++) fac[i] = (fac[i - 1] * i) % mod; for (int i = 2; i <= n; i++) inv[i] = (mod - mod / i) * inv[mod % i] % mod; for (int i = 1; i <= n; i++) inv[i] = (inv[i - 1] * inv[i]) % mod; } signed main() { n = read(); init(); len = 1; A[1] = B[1] = C[1] = D[1] = 1; for (int i = 2; i <= n; i++) { if (t * 2 < i) { for (int j = 0; j <= i; j++) B[i] = (B[i] + C[i - j] * D[j] % mod) % mod; B[i] = (B[i] * fac[i - 1]) % mod; A[i] = (B[i] * 2) % mod; C[i] = (A[i] * inv[i]) % mod; D[i] = (B[i] * inv[i - 1]) % mod; } else { B[i] = c[i]; for (int j = 0; j < i - t; j++) B[i] = (B[i] + C[i - j] * D[j] % mod) % mod; for (int j = t + 1; j <= i; j++) B[i] = (B[i] + C[i - j] * D[j] % mod) % mod; B[i] = (B[i] * fac[i - 1]) % mod; A[i] = (B[i] * 2) % mod; C[i] = (A[i] * inv[i]) % mod; D[i] = (B[i] * inv[i - 1]) % mod; } if (i - t == T) { while (len <= i * 2) len <<= 1; for (int i = 0; i <= len; i++) a[i] = C[i], b[i] = D[i]; mul(len, a, b); t = i; } } printf("%lld\n", (A[n] + mod - 2) % mod); return 0; }
posted @   David24  阅读(235)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示