周测题解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;
}
posted @ 2021-09-04 21:02  Jijidawang  阅读(8)  评论(0)    收藏  举报  来源