AtCoder Beginner Contest 346
最刺激的一集。
尝试挑战手速极限,用了 57s 做 A。但是好像还是很慢。
然后做 B,仍然想挑战手速。结果一眼出思路只要把 wbwbwwbwbwbw
多重复几遍就可以代替「无限长」。
很快就写完了。然后交了三发罚时。后来发现我复制若干遍 wbwbwwbwbwbw
的时候好像复制错了/ll /ll
过 B 之前很快过掉了 C, D。其中 D 有一发罚时是因为 DP 的边界错了。
过了 A~D 后看 E。感觉没什么思路。但是想到了如果这一行/列被覆盖了多次,那么只有最后一次覆盖是有效的。
于是想先实现这个过程。发现如果要这么做,必然需要将所有操作倒序处理。于是就莫名奇妙地联想到了白雪皑皑,发现倒序处理是正确的。延续着涂色的思路打出了正解。
此时是 43min。还有一个小时。
看 F 题。发现太典了。题目中字字句句都在提示二分,而二分的 check 又是一个入门贪心。想了一两分钟就开始写了。
越写越觉得 check 太过麻烦,但是还是硬着头皮写下来了份 140+ 的代码。没有调试,发现比答案少一。加上一后直接交了。结果 AC15 WA35。简单修改变成了 AC26 WA24。
然后发现加一绝对是错误的。去掉后开始疯狂调试 + 补充代码。赛时我的代码中写了若干个二分,没有把二分封装成函数,结果赛后发现每个二分都写错了。除此之外小错误和恶心边界比比皆是,写着写着就有些崩溃了。
于是硬生生地冲着 CP Editor 写了一个小时。比赛结束。
这份代码已经丑陋到 170+ 了。想了想还是重构代码吧。赛后一小时终于过了。
这里列几个犯的错误:
int getp(int l, int r, int k, int w) { // [l, r] 中第一个 p 使得 [l, p] 中 w 出现 k 次
int pos = -1, L = l; // 要把最开始的 l 记录下来
while (l <= r) {
int mid = l + r >> 1, S = calc(L/*这里不能用 l,需要用最开始的 L*/ , mid, w);
// if (S == k) return mid; 是错误的。
if (S >= k) pos = mid, r = mid - 1;
else l = mid + 1;
}
return pos;
}
bool chk(ll k) {
pair<ll, int> cur = {1, 0};
for (int i = 1; i <= lb; ++ i ) {
cur = nxt(cur.first, cur.second, b[i] - 'a', k);
if (cur.first > n) return false;// 如果不写这行,在最后写 return cur.first <= n,那么 cur.first 会超出 long long 的范围
}
return true;
}
D - Gomamayo Sequence
- 给定一个长度为
的 串 。 - 如果
是「IOIAKer」,当且仅当满足以下条件:- 有且仅有一个整数
满足 且 。
- 有且仅有一个整数
- 对于每个
,执行以下操作:- 花费
的代价将 取反。
- 花费
- 求将
变成「IOIAKer」所需的最小代价。 , 。
一眼 DP。
设状态
转移可以枚举上一位填的什么。然后根据是否需要翻转计算代价。
f[i][0][0] = f[i - 1][1][0] + (s[i] == '1') * c[i];
f[i][1][0] = f[i - 1][0][0] + (s[i] == '0') * c[i];
f[i][0][1] = min(f[i - 1][1][1], f[i - 1][0][0]) + (s[i] == '1') * c[i];
f[i][1][1] = min(f[i - 1][0][1], f[i - 1][1][0]) + (s[i] == '0') * c[i];
int n, c[N];
char s[N];
int f[N][2][2]; // 将前 i 个位置变成 0101010...,
void Luogu_UID_748509() {
scanf("%lld%s", &n, s + 1);
for (int i = 1; i <= n; ++ i ) scanf("%lld", c + i);
f[2][0][0] = (s[1] == '0') * c[1] + (s[2] == '1') * c[2];
f[2][1][0] = (s[1] == '1') * c[1] + (s[2] == '0') * c[2];
f[2][0][1] = (s[1] == '1') * c[1] + (s[2] == '1') * c[2];
f[2][1][1] = (s[1] == '0') * c[1] + (s[2] == '0') * c[2];
for (int i = 3; i <= n; ++ i ) {
f[i][0][0] = f[i - 1][1][0] + (s[i] == '1') * c[i];
f[i][1][0] = f[i - 1][0][0] + (s[i] == '0') * c[i];
f[i][0][1] = min(f[i - 1][1][1], f[i - 1][0][0]) + (s[i] == '1') * c[i];
f[i][1][1] = min(f[i - 1][0][1], f[i - 1][1][0]) + (s[i] == '0') * c[i];
}
fout << min(f[n][0][1], f[n][1][1]) << '\n';
}
E - Paint
- 有一个
的网格,最开始每个格子的颜色都是 。 - 按顺序执行
个操作,每个操作形如 :- 若
,将第 行全部涂成 颜色。 - 若
,将第 列全部涂成 颜色。
- 若
- 求最后每种颜色的出现次数。
。
首先观察到,如果对某一行/列进行了多次染色,那么只有最后一次染色是有效的,所以我们只保留最后一次染色。如果这一行/列自始至终没有没染过色,相当于最开始为它全染成
这样,我们就相当于对于每一行和每一列都进行了恰好一次染色操作。那么做法是将所有操作倒序处理。
假如在「在第
所以我们在倒序处理操作的过程中,记录已经操作过多少次行/列,并分解计算即可。
int n, m, k;
int nn, mm;
int res[N];
struct Node {
int op, a, x;
}a[N];
void Luogu_UID_748509() {
fin >> n >> m >> k;
for (int i = 1; i <= k; ++ i ) {
fin >> a[i].op >> a[i].a >> a[i].x;
}
map<pair<int, int>, bool> mp;
auto work = [&](int op, int a, int x) {
pair<int, int> t = {op, a};
if (mp.count(t)) {
return;
}
mp[t] = true;
if (op == 1) {
res[x] += m - nn;
++ mm;
}
else {
res[x] += n - mm;
++ nn;
}
};
for (int i = k; i; -- i ) work(a[i].op, a[i].a, a[i].x);
for (int i = 1; i <= n; ++ i ) work(1, i, 0);
vector<pair<int, int> > ans;
for (int i = 0; i <= 200000; ++ i )
if (res[i]) ans.push_back({i, res[i]});
fout << ans.size() << '\n';
for (auto t : ans) {
fout << t.first << ' ' << t.second << '\n';
}
}
F - SSttrriinngg in StringString
- 对于一个字符串
和一个非负整数 ,定义 表示将 重复 次得到的字符串, 表示将 的每个字符重复 次得到的字符串。例如当 时, 。特别的,当 时, 为空串。 - 给定两个字符串
和一个正整数 。求最大的非负整数 满足 是 的子序列。 , 。
子序列是具有传递性的。对于字符串
容易发现
问题就转化成了,已知
所以我们的任务是求解
这是一个简单贪心。在判断「字符串
同理,我们想逐位考虑
定义
上述「调整」过程大致可分三步:
- 将第
个 的位置 匹配完; - 匹配若干个完整的
; - 将
中没匹配完的字符从头与 匹配。
实现上,我们需要维护
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
ll n;
int la, lb;
char a[N], b[N];
int sum[N][26];
int lst[26];
int calc(int l, int r, int w) {
return sum[l][w] - sum[r + 1][w];
}
int getp(int l, int r, int k, int w) {
int pos = -1, L = l;
while (l <= r) {
int mid = l + r >> 1, S = calc(L, mid, w);
if (S >= k) pos = mid, r = mid - 1;
else l = mid + 1;
}
return pos;
}
pair<ll, int> nxt(ll x, int y, int w, ll k) {
if (sum[y + 1][w] >= k) return make_pair(x, getp(y + 1, la, k, w));
k -= sum[y + 1][w], x ++ , y = 0;
if (k % sum[1][w] == 0) return make_pair(x + k / sum[1][w] - 1, lst[w]);
x += k / sum[1][w], y = 0, k %= sum[1][w];
return make_pair(x, getp(1, la, k, w));
}
bool chk(ll k) {
pair<ll, int> cur = {1, 0};
for (int i = 1; i <= lb; ++ i ) {
cur = nxt(cur.first, cur.second, b[i] - 'a', k);
if (cur.first > n) return false;
}
return true;
}
signed main() {
scanf("%lld%s%s", &n, a + 1, b + 1);
la = strlen(a + 1), lb = strlen(b + 1);
for (int i = la; ~i; -- i ) {
for (int j = 0; j < 26; ++ j ) sum[i][j] = sum[i + 1][j];
if (i) sum[i][a[i] - 'a'] ++ ;
}
for (int i = 1; i <= la; ++ i ) lst[a[i] - 'a'] = i;
for (int i = 1; i <= lb; ++ i )
if (!sum[1][b[i] - 'a']) return puts("0"), 0;
ll l = 1, r = 1e18, res = 0;
while (l <= r) {
ll mid = l + r >> 1;
if (chk(mid)) res = mid, l = mid + 1;
else r = mid - 1;
}
printf("%lld\n", res);
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战