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;
}

完成!!!~~~

posted @ 2026-01-14 21:01  MichaelZeng  阅读(15)  评论(0)    收藏  举报