Codeforces Round 972 (Div. 2)
写在前面
比赛地址:https://codeforces.com/contest/2005。
唉唉赛时 C 烂掉了要不然要上大分现在只能上小分了——如果 C 最后没 fst 的话呃呃现在只能掉小分了。
妈的所以我赛时 C 吃了 6 发最后还是没过啊,真是坏透了。
A 签到
发现应当尽量不然两个相同的字符之间有任何字符,比如 a....a
,否则一定会构成超级多回文子序列。
然后发现每种字符应当尽可能少,于是考虑将数量平均分到所有字符上,然后按顺序排列即可。
复制复制// /* By:Luckyblock */ #include <bits/stdc++.h> #define LL long long //============================================================= //============================================================= //============================================================= int main() { //freopen("1.txt", "r", stdin); std::ios::sync_with_stdio(0), std::cin.tie(0); int T; std::cin >> T; while (T --) { int n; std::cin >> n; char s[10] = "aeiou"; int len = n / 5, r = n % 5; for (int i = 0; i < 5; ++ i) { for (int j = 1; j <= len; ++ j) { std::cout << s[i]; } if (r) std::cout << s[i], -- r; } std::cout << "\n"; } return 0; }
B1/B2 模拟,结论,二分
发现仅需考虑与逃脱者相邻的两个老师即可:
- 若逃脱者左边没有老师,则最优的方案是逃到最左边然后呆着;右侧没有老师同理;
- 否则逃脱者应当逃到两个老师位置的中点。
于是考虑用 set 维护所有老师的位置,每次查询前驱后继, 计算答案即可。
B2 代码:
// /* By:Luckyblock */ #include <bits/stdc++.h> #define LL long long const int kN = 2e5 + 10; //============================================================= int n, m, q; int b[kN]; std::set<int> s; //============================================================= int query(int x_) { if (x_ < b[1]) return b[1] - 1; if (x_ > b[m]) return n - b[m]; auto p = s.lower_bound(x_); auto q = p; -- q; return (*p - *q) / 2; } //============================================================= int main() { //freopen("1.txt", "r", stdin); std::ios::sync_with_stdio(0), std::cin.tie(0); int T; std::cin >> T; while (T --) { std::cin >> n >> m >> q; s.clear(); for (int i = 1; i <= m; ++ i) std::cin >> b[i], s.insert(b[i]); std::sort(b + 1, b + m + 1); while (q --) { int x; std::cin >> x; std::cout << query(x) << "\n"; } } return 0; }
C DP
呃呃裸 DP,题面写的一坨理解错了叫我嗯吃六发,然后还 fst 了真是被这题鲨完了。
发现实际有贡献的字符集大小仅有 5,考虑记 narek
分别为 。考虑预处理 表示字符串 中 narek
的数量。
发现可以分别考虑每个字符串怎么取,仅需知道在这一字符串第一个取哪一个字符,即可唯一确定取完这一段可以拿到几个字符,以及最后取到的是哪个字符。
于是考虑先预处理一下 ,分别表示当前取的是第 个字符串,第一个必须取字符 ,可以取到的字符的最多数量,以及最后取到的是哪个字符。字符集大小只有 5 直接大力 枚举预处理即可。
又限定不能交换选择的字符串的顺序,于是考虑 DP,记 表示当前最后选的不大于第 个字符串,对这一字符串按照给定规则选完字符之后,选的最后一个字符为 ,可以获得的最大价值。转移时考虑上一个字符串最后的字符 ,则有显然的转移:
统计答案时,考虑枚举最后一段选的是哪个字符串 ,以及最后一段的可能不完整的 narek
的长度 即可,若最后一段不完整则需要减掉他们的贡献,则有:
复杂度 级别。
// /* By:Luckyblock */ #include <bits/stdc++.h> #define LL long long const int kN = 1e6 + 10; const char ch[10] = "narek"; const int kInf = 1e6; //============================================================= int n, m; std::string s[kN]; int all[kN]; int f[kN][6], cnt[kN][6], end[kN][6]; int yes[500], rk[500]; //============================================================= //============================================================= signed main() { // freopen("1.txt", "r", stdin); for (int i = 0; i < 5; ++ i) yes[(int) ch[i]] = 1, rk[(int) ch[i]] = i; std::ios::sync_with_stdio(0), std::cin.tie(0); int T; std::cin >> T; while (T --) { std::cin >> n >> m; for (int i = 1; i <= n; ++ i) { std::cin >> s[i]; all[i] = 0; for (auto c: s[i]) if (yes[(int) c]) ++ all[i]; s[i] = "$" + s[i]; } for (int i = 1; i <= n; ++ i) { for (int j = 0; j < 5; ++ j) { cnt[i][j] = 0, end[i][j] = -1; for (int k = 1, l = s[i].length(), now = j; k < l; ++ k) { if (!yes[(int) s[i][k]]) continue; if (rk[(int) s[i][k]] != now) continue; ++ cnt[i][j], end[i][j] = now; now = (now + 1) % 5; } } } int ans = 0; for (int i = 0; i <= n; ++ i) { for (int j = 0; j < 5; ++ j) { f[i][j] = -kInf; } } f[0][4] = 0; for (int i = 1; i <= n; ++ i) { for (int k = 0; k < 5; ++ k) f[i][k] = f[i - 1][k]; for (int k = 0; k < 5; ++ k) { int p = (k + 1) % 5; if (cnt[i][p] == 0) continue; f[i][end[i][p]] = std::max(f[i][end[i][p]], f[i - 1][k] + cnt[i][p] - (all[i] - cnt[i][p])); if (end[i][p] == 4) ans = std::max(ans, f[i][end[i][p]]); else ans = std::max(ans, f[i][end[i][p]] - 2 * (end[i][p] + 1)); } } std::cout << ans << "\n"; } return 0; } /* 1 1 6 narekn 1 1 11 nareknareek 1 2 5 zzrrn areka */
D 数学,结论
周所周知区间 可以 ST 表 预处理 查询,并且常数很小。
众所周知序列 是有单调性的,序列越长 单调不降,且每次下降至少令 除 2。
考虑枚举被操作区间的左端点 ,然后考虑右端点 的取值对答案的影响。发现此时可以将两个数列分成六段:
- :因为枚举左端点已确定。
- ;
- ;
由上述结论可知,后四段至多仅会随着 的移动变化 次,则有贡献的 仅有至多 段。于是考虑枚举左端点 ,然后大力二分求得上述至多 段的断点并查询。
总时间复杂度 级别,赛时可过但是赛后被卡了呃呃。
每次二分求得端点这个 要不得,考虑能否直接动态维护这 段,并每次仅加入新增贡献的段。发现仅需考虑倒序枚举 ,然后考虑将所有段与新加入贡献的 与它们取 ,即可 维护所有段。之后再枚举所有段的断点统计贡献即可。
总时间复杂度 级别。
E1 DP
发现每个人选的数是唯一确定的,一个显然的想法是考虑当前选到哪个数,且当前这个人要选什么位置,则下一个人仅需考虑该位置右下的矩形即可。
于是想到一个显然的 DP,设 表示当前要选第 个数了,第 0/1 个玩家操作,这个玩家这一步选择 ,是否必胜,则转移时仅需考虑 这些状态里有没有 1 即可。发现是个矩形求和的形式,仅需额外对 维护一个二维前缀和即可。
因为每个人选的数是唯一确定的,发现 的第一维是无必要的,滚一下就行了。
总时间复杂度 级别。
// /* By:Luckyblock */ #include <bits/stdc++.h> #define LL long long const int kN = 300 + 10; //============================================================= int l, n, m, a[kN], b[kN][kN]; int f[2][kN][kN], sum[2][kN][kN]; //============================================================= void init() { for (int p = 0; p <= 1; ++ p) { for (int i = 1; i <= n + 1; ++ i) { for (int j = 1; j <= m + 1; ++ j) { f[p][i][j] = sum[p][i][j] = 0; } } } } //============================================================= int main() { // freopen("1.txt", "r", stdin); std::ios::sync_with_stdio(0), std::cin.tie(0); int T; std::cin >> T; while (T --) { std::cin >> l >> n >> m; for (int i = 1; i <= l; ++ i) std::cin >> a[i]; for (int i = 1; i <= n; ++ i) { for (int j = 1; j <= m; ++ j) { std::cin >> b[i][j]; } } init(); for (int k = l; k; -- k) { int p = (k & 1); for (int i = 1; i <= n; ++ i) { for (int j = 1; j <= m; ++ j) { f[p][i][j] = 0; if (b[i][j] != a[k]) continue; if (sum[p ^ 1][i + 1][j + 1] == 0) f[p][i][j] = 1; } } for (int i = n; i; -- i) { for (int j = m; j; -- j) { sum[p][i][j] = f[p][i][j]; sum[p][i][j] += sum[p][i + 1][j] + sum[p][i][j + 1] - sum[p][i + 1][j + 1]; } } } int flag = 0; for (int i = 1; i <= n; ++ i) { for (int j = 1; j <= m; ++ j) { if (f[1][i][j] == 1) { flag = 1; break; } } if (flag) break; } std::cout << (flag ? "T" : "N") << "\n"; } return 0; }
E2 贪心,DP
感觉 E1 的做法已经很优美了,但是不可避免地需要枚举 ,但 E2 仅保证了 的上界,于是需要把 从复杂度里删掉。
写在最后
学到了什么:
- C:注意数据范围中保证的是什么东西的上界!!!
妈的什么时候才能上黄、、、
然后夹带一下私货吧呜呜,关注 RESCAT 喵关注 RESCAT 谢谢喵:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!