USACO 25FEB 铜组题解

铜组 25FEB 所有题目链接

T1

大模拟。

思路挺简单的,就是调试比较麻烦。


初始化部分:

  • 只需要处理左上角四分之一的点 \([r, c]\),并计算它与对称点的 # 数量记为 \(cnt_{r, c}\)
  • 目标是使四个对称点的字符保持相同(全是 . 或全是 #)并计算 \(\min(cnt_{r, c}, 4 - cnt_{r, c})\) 来找到最少修改次数。

模拟修改部分:

对于每次修改,如果是将 # 改为 .,就 \(cnt_{x, y} - 1\),反之 \(+1\)

等于是重做初始化部分第一步。

然后将新计算出的答案加入即可。

\(ans \gets ans + \min(cnt_{r,c}, 4 - cnt_{r,c}) - cnt_{r,c}'\)

注:\(cnt_{r,c}'\) 是上一次计算出的 \(cnt_{r,c}\) 值。

这条代码的意思是,在改变一个字符后(坐标设为 \([r,c]\))重新计算 \(cnt_{r,c}\) 与原先 \(cnt_{r,c}'\) 进行对比然后将计算出来多或少的值更新至 \(ans\) 中。

注:由于有特判,这个修改的字符绝对是在左上角部分。

例如:

#.
..

初始化部分的 \(ans\) 计算为 \(1\),假设修改左上角 #.

..
..

更新 \(cnt_{1,1} \gets cnt_{1, 1}-1\),此时根据修改后的 \(cnt_{1,1}\)(值为 \(0\)),修改 \(ans\) 值,\(ans \gets ans + \min(0, 4-0) - 1\)\(ans\) 修改值为 \(0\)


#include <bits/stdc++.h>
using namespace std;

#define ll long long
int n, u;
string mp[2005];

int main()
{
    cin >> n >> u;
    for (int i = 0; i < n; i++)
        cin >> mp[i];

    int cnt[2005][2005];
    memset(cnt, 0, sizeof(cnt));
    ll ans = 0;
    for (int r = 0; r < n / 2; r++)
	{
        for (int c = 0; c < n / 2; c++)
		{
            if (mp[r][c] == '#')
				cnt[r][c]++;
            if (mp[r][n - 1 - c] == '#')
				cnt[r][c]++;
            if (mp[n - 1 - r][c] == '#')
				cnt[r][c]++;
            if (mp[n - 1 - r][n - 1 - c] == '#')
				cnt[r][c]++;
            ans += min(cnt[r][c], 4 - cnt[r][c]);
        }
    }
    cout << ans << "\n";
    for (int i = 0; i < u; i++)
	{
        int r, c;
        cin >> r >> c;
        int rr = min(r - 1, n - r);
        int cc = min(c - 1, n - c);
        int x = min(cnt[rr][cc], 4 - cnt[rr][cc]);

        if (mp[r - 1][c - 1] == '#')
		{
            mp[r - 1][c - 1] = '.';
            cnt[rr][cc]--;
        }
		else
		{
            mp[r - 1][c - 1] = '#';
            cnt[rr][cc]++;
        }
        ans += min(cnt[rr][cc], 4 - cnt[rr][cc]) - x;
        cout << ans << "\n";
    }
    return 0;
}

T2

思路

要让数组 \(a\) 中的 \(mex = i\),我们需要让数组 \(a\) 满足两个条件:

  • \(\forall x \in \{0, 1, \dots, i-1\}, \quad x \in a\)
  • \(i \notin a\)

对于代码实现,我们可以先预处理一个数组 \(cnt\)\(cnt_i\) 表示数组 \(a\) 中数字 \(i\) 的个数。

对于每个 \(mex\) 的询问,我们先遍历 \(0 \sim i - 1\),查看有多少个数缺失,设为 \(miss\)(条件 1);然后再调取 \(cnt_i\) 查看有多少它本身在数组 \(a\)(条件 2)。

此时,我们已经得到了解法,但是这种解法在每次解决 \(\forall x \in \{0, 1, \dots, n\}, \quad x = mex\) 时都要重新计算 \(miss\),时间复杂度 \(O(n^2)\),显然过不了 \(2 \times 10^5\),我们需要优化。

而显然,\(miss\) 可以预处理进行优化,具体思路类似递推或 dp。

\(miss_i\)\(0 \sim i - 1\) 中有多少个数缺失,而 \(miss_i\) 可以从 \(miss_{i - 1}\) 转移:

\( miss_i = \begin{cases} miss_{i-1} + 1, & \text{if } cnt_{i-1} \neq 0 \\ miss_{i-1}, & \text{if } cnt_{i-1} = 0 \end{cases} \)

时间复杂度约为 \(O(n)\),可以通过。

代码

#include <bits/stdc++.h>
using namespace std;

int n;
vector<int> a;
int cnt[200005];
int miss[200005];

int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		int x;
		cin >> x;
		a.push_back(x);
		cnt[x]++;
	}
	sort(a.begin(), a.end());
	for (int i = 1; i <= n; i++)
	{
		if (cnt[i - 1] == 0)
			miss[i] = miss[i - 1] + 1;
		else
			miss[i] = miss[i - 1];
	}
	
	int q = 0;
	while (q <= n)
	{
		int now = cnt[q] - miss[q];
		if (now >= 0)
			cout << cnt[q] << "\n";
		else
			cout << cnt[q] + abs(now) << "\n";
		q++;
	}
	return 0;
}

T3

思路

本题解使用简单 dp 解决本问题。

\(dp_{i, j}\) 为区间 \([i, j]\) 中使用的 print 代码个数,判断 \(\text{if } dp_{1, n} \le k\)

对于长度为 \(1\) 的子序列,\(dp_{i, i} = 1\),指使用一个 print 语句输出字符。

对于长度大于 \(1\) 的子序列,设子序列长度为 \(len\),从 \(2 \sim n\);左右边界为 \([i, j],1 \le i \le n - len + 1\)

  1. 在子序列中遍历一个可能的分隔点,设为 \(l\)\(i \leq l \leq j\);初始化 \(dp_{i, j} = \min(dp_{i, j}, dp_{i, l} + dp_{l + 1, j})\)
  2. 然后开始检查子序列是否可以由子序列中的一个子序列拼接而来。
  3. 枚举子序列中的子序列的长度,即周期长度,设为 \(l\)\(1 \le l < len\)\(len \bmod l = 0\)
  4. 检查周期中每个位置 \(p\)\(i \le p \leq j\),是否满足 \(a_p = a_{i + (p - i) \bmod l}\),即为检查子序列中每 \(l\) 位是否与周期 \(p_{i} \sim p_{i + l - 1}\) 相同。如果全部满足,则更新 \(dp_{i, j} = \min(dp_{i, j}, dp_{i, i + l - 1})\)

输出:判断 \(dp_{1, n} \le k\) 即可。

时间复杂度约为 \(O(T \times n^3 \times f(n))\) 左右,其中 \(f(n)\) 表示 \(n\) 的因子数量。

代码

#include <bits/stdc++.h>
using namespace std;

int t;
int n, k;

int main()
{
    int t;
    cin >> t;
    while (t--)
	{
        int n, k;
        cin >> n >> k;
        int a[105];
        for (int i = 1; i <= n; i++)
            cin >> a[i];
        int dp[105][105];
        for (int i = 1; i <= n; i++)
        {
        	for (int j = 1; j <= n; j++)
        		dp[i][j] = 105;
		}
        for (int i = 1; i <= n; i++)
            dp[i][i] = 1;
        
        for (int len = 2; len <= n; len++)
		{
            for (int i = 1; i <= n - len + 1; i++)
			{
                int j = i + len - 1;
                for (int l = i; l < j; l++)
                    dp[i][j] = min(dp[i][j], dp[i][l] + dp[l + 1][j]);

                int x = len;
                for (int l = 1; l < x; l++)
				{
                    if (x % l != 0)
                        continue;
                    bool flag = true;
                    for (int p = i; p <= j; p++)
					{
                        if (a[p] != a[i + (p - i) % l])
						{
                            flag = false;
                            break;
                        }
                    }
                    if (flag)
                        dp[i][j] = min(dp[i][j], dp[i][i + l - 1]);
                }
            }
        }
        cout << (dp[1][n] <= k ? "YES" : "NO") << "\n";
    }
    return 0;
}
posted @ 2025-03-23 11:34  George0915  阅读(48)  评论(0)    收藏  举报