周测题解9.3
Information Onceaweek Inzhizhen boomed again and again
T1
经典数论几何题。
一个最基本的贪心策略就是先横向摆(此时还剩一个 $n×(n\bmod m)$的空)。
然后再在空里竖着摆(此时剩 $(n \bmod m)^2$ 的空),就摆不下了。
因为OI不用证明贪心这个贪心是最优的,答案就是 $(n \bmod m)^2$。
T2
懒得再写一遍了,这篇的T4就是。
T3
队列应用题。(虽然我考场上没写出来)
首先先说一点,枚举区间是不可避免的。
但枚举区间不一定要用 $O(n^2)$ 的大暴力。
这里就需要动态维护(不知道用词对不对)两个端点。
那么我们就可以用到队列了。
思路
首先,肯定是依次往队里压,每次压完判断一下队里的是否可以作为答案。
这就是第一个难点了:怎么判断队里的东西可不可以作为答案呢?
我们需要知道,这题的目的是去掉(替换)该去掉的点。
那么该去掉的点有几个呢?
我们知道,每个点都要剩下 $n/4$ 个。
那么用原有点数 - 应该剩下的点数就是该去掉的点数了。
即对于字符 $c$,$count(c)-n/4=c$ 该去掉的点数。(其中 $count$ 是统计 s 中 c 的个数)。
判断完了队中元素是否可以作为答案,那怎么维护左端点呢?
这又是另一个难点了:
我们知道,这个区间是越短越好的。
所以队头只要每压一次就一直弹出,就可以保证区间最小。
然而肯定不能一直弹,我们就可以想到:
一直弹到不能弹为止。(不可以作为答案)
代码
#include <iostream>
#include <queue>
#include <unordered_map>
#include <string>
#include <algorithm>
using namespace std;
unordered_map<char, int> nd, iq;int n, ans = 1e9;string s;queue<char> q;
bool chk()
{
for(char i = 'A';i <= 'D';++i)
if(iq[i] < nd[i]) return 0;
return 1;
}
int main()
{
cin >> n >> s;
for(char i = 'A';i <= 'D';++i)
nd[i] = count(s.begin(), s.end(), i) - n / 4;
for(int i = 0;i < n;++i)
{
q.push(s[i]);++iq[s[i]];
while(chk())
{
ans = min(ans, int(q.size()));
--iq[q.front()];q.pop();
}
}
cout << ans;
return 0;
}

浙公网安备 33010602011771号