USACO202601 Silver C 题解
前言
这次USACO Silver 打爆了,就对了1题(就这题)
赛时被bug硬控1个多小时
题面
老牛Bessie藏了一个我们不知道的仅包含\(0\)和\(1\)数组\(a\),长度为\(n\),现在Bessie给了我们一个数组\(b\),长度为\(n-k+1\), 已知\(b_i = (\displaystyle{\sum_{j=i}^{i+k-1} a_j}) \mod 2\),用人话说就是在\(a\)中以\(i\)为左端的长度为\(k\)的窗口的数字之和除以2的余数。
现在给定\(n,k\)和\(b\)数组,求出\(a\)最少有多少个\(1\),最多有多少个\(1\)。
思路
因为\(b_i = (\displaystyle{\sum_{j=i}^{i+k-1} a_j}) \mod 2\), \(b_{i+1} = (\displaystyle{\sum_{j=i+1}^{i+k} a_j}) \mod 2\)
所以\(b_{i+1}-b_i = (a_{i+k} - a_i + 2) % 2\)
所以当\(b_i = b_{i+1}\)时,\(a_i = a_{i+k}\)
当\(b_i \neq b_{i+1}\)时,\(a_i \neq a_{i+k}\)
同时这些结论反向也成立。
注意到这些都是\(a_i\)与\(a_{i+k}\)的关系,我们可以把所有的位置按照\(\mod k\)分成\(k\)组
而因为\(a_i\)仅为\(0\)或\(1\),所以当\(b_i \neq b_{i+1}\)时,\(a_i = 1 - a_{i+k}\)
所以每组中只要确定一个\(a_i\)的值,整个组都能确定。
上面说的结论反向也成立,所以我们只要确定\(a\)的前\(k\)个元素的值,且满足\(b_1\)的约束条件,此时每组都有一个确定值,后面的\(n-k\)个按照上述要求就可以全部确定了。
接下来我们定义一个\(x\)数组和\(y\)数组,\(x_{i,0}\)表示(前\(k\)位置中)第\(i\)个位置放0时这一组有多少个1,\(x_{i,1}\)表示(前\(k\)位置中)第\(i\)个位置放1时这一组有多少个1(包括第一个),\(y_{i,0}\)实时计算(前\(k\)位置中)第\(i\)个位置放0时当前放置的是0还是1, \(y_{i,1}\)实时计算(前\(k\)位置中)第\(i\)个位置放1时当前放置的是0还是1。
有了这些之后,我们遍历每个位置,计算\(y\)并累加到\(x\)(此部分具体见代码)
最后,就是确定前\(k\)个,先看最小值,我们记下1比0组内所需1少的位置的个数,并累加所有位置0 1中少的那个数字。如果1比0少的个数(即不考虑\(b_1\)的情况下前\(k\)个有多少个1)满足\(b_1\)的约束,即可直接将累加的答案记下,否则记下最少要减少多少的答案。
最大值同理
代码:
#include <bits/stdc++.h>
using namespace std;
int x[1000001][2];
int y[1000001][2];
int main()
{
int t;
cin >> t;
while (t--)
{
memset(x, 0, sizeof(x)); // 记得清空 (考场上调好久)
memset(y, 0, sizeof(y)); // 记得清空 (考场上调好久)
int n, k;
cin >> n >> k;
string s;
cin >> s;
int m = s.size();
s = ' ' + s;
for (int i = 1; i <= n; i++)
{
x[i][1] = 1; // 初始化
y[i][1] = 1;
}
for (int i = 2; i <= m; i++)
{
if (s[i] != s[i - 1]) // 与i-k不一样
{
y[(i - 1) % k + 1][0] = 1 - y[(i - 1) % k + 1][0]; // (i - 1) % k + 1 计算的是这个位置所在组的第一个位置,更新
y[(i - 1) % k + 1][1] = 1 - y[(i - 1) % k + 1][1]; // 同理,更新
x[(i - 1) % k + 1][0] += y[(i - 1) % k + 1][0]; // 累加
x[(i - 1) % k + 1][1] += y[(i - 1) % k + 1][1]; // 累加
}
else
{
// 与i-k一样,所以y不用更新
x[(i - 1) % k + 1][0] += y[(i - 1) % k + 1][0]; // 累加
x[(i - 1) % k + 1][1] += y[(i - 1) % k + 1][1]; // 累加
}
}
int c = 0, mn = 1e9, mx = -1e9; // c记录的是1比0多的个数
int ans = 0; // 1最多多少
int ans2 = 0; // 1最少多少
for (int i = 1; i <= k; i++)
{
if (x[i][1] > x[i][0]) // 1比0多
{
ans += x[i][1]; // 统计
ans2 += x[i][0]; // 统计
c++; // 记录
mn = min(mn, x[i][1] - x[i][0]); // 分开求一下
}
else
{
ans += x[i][0];
ans2 += x[i][1];
mx = max(mx, x[i][1] - x[i][0]);
}
}
if (c % 2 != s[1] - '0') ans = max(ans - mn, ans + mx); // 判断,如果不满足s1就改一个即可
if ((k - c) % 2 != s[1] - '0') ans2 = min(ans2 + mn, ans2 - mx); // k-c就是1比0少的个数,修改同上
cout << ans2 << ' ' << ans << endl; // 输出
}
return 0;
}
完成!!!~~~

浙公网安备 33010602011771号