题目
思路
感谢@kkksc03 的博客提供了思路;感谢@哔哩哔哩 的博客提供了代码。
2 n 2n 2n 个人,只有 n n n 分钟,所以 每一分钟,两个厕所都在使用中。
简化题意:操作 n n n 次,每次拿出两个等待队列的元素删掉。删掉的规则:
- 一定要删掉队列头。
- 如果队头是 M M M ,则再删去第一个 F F F (毕竟两个绅 ♂ ♂ ♂ 士不能共同如厕)。
- 如果队头是 F F F ,则再删去新的队头(反正还剩一个公用厕所)。
我们的目的是,每次都能成功的删去两个元素。
有几个很显然的结论(但是很重要):
- 不会有两个男性同时如厕(我相信这些绅 ♂ ♂ ♂ 士都是洁身自好的)。
- 只有情况一可能导致失败(因为情况二是直接删去最前面的两个元素,只要有元素剩余,就一定可以成功删去;而情况一要求后面还有一个 F F F )。
结论一
这句话很简单:如果队列中男多女少,那么失败。这是很显然的——毕竟不会同时删去两个 M M M ,男女比例只会愈发失调。
更有意思的是,如果会失败,那么一定在某一瞬间出现了男多女少的状况。根据 显然结论二
,失败时一定是情况一,即:队头是男性,但后方没有女性了。这不就是男多女少的情况嘛!
结论二
如果存在一个后缀,满足男性的数量 = = = 女性的数量 + 2 +2 +2 ,那么一定不能成功的如厕。
注:如果存在一个后缀,满足男性的数量 ≥ \ge ≥ 女性的数量 + 2 +2 +2 ,那么也一定存在一个后缀,使其取等。
试着去证明它吧。考虑一个这样的后缀,它一定是以 M M M 作为第一个;否则可以将其起始端点右移。
对于队头的
M
M
M ,他可能会与之前的某个
F
F
F 一同从队列里删去,也可能和后方的
F
F
F 。无论哪种情况,都要注意到这一点:剩下的人,一定是男多女少——原本有
x
+
2
x+2
x+2 个男性、
x
x
x 个女性,第一个男性离开后,剩下的数量为
⟨
x
+
1
,
x
⟩
\langle x+1,x\rangle
⟨x+1,x⟩ 或者
⟨
x
+
1
,
x
−
1
⟩
\langle x+1,x-1\rangle
⟨x+1,x−1⟩ —— 结论一
中已经说了,男多女少,是一定无法完成任务的。
结论三
如果不存在一个后缀,满足男性的数量 = = = 女性的数量 + 2 +2 +2 ,那么一定可以成功的如厕。
试着去证明它吧。为了方便论述,我们讨论男性数量 − - − 女性数量的值——它只要一直不等于 2 2 2 ,就可以成功如厕。
一开始一定有至少一个女性(否则男
−
-
− 女
≥
2
\ge 2
≥2 ),所以不会一来就失败。如果队头是女性,没有任何分析的意义:从队头直接拿走两个,对于剩下元素的后缀没有改变。所以我们分析队头是男性的情况。如图。
操作之后,对于
F
F
F 右边的,男
−
-
− 女没有发生变动。我们只需要考虑红色的部分(注意:它们都是
M
M
M ;这是由操作的要求规定的)。
正如我用黑笔所写的差,由于它们忘掉了队头的 M M M (导致 c n t M cnt_M cntM 减小),红色部分原本的差一定 < x − y <x-y <x−y (即队头的值); F F F 的离开又让 c n t F cnt_F cntF 减小了 1 1 1 ,所以差增大了一。最终,这个差 ≤ x − y < 2 \le x-y<2 ≤x−y<2 ,所以我们得到的仍然是一个满足条件的队列。
也就是说,对于一个满足该条件的序列,第一次操作不会失败,并且操作后仍然满足条件。所以最终可以成功的完成如厕任务。
总结结论
总结一下:将男性设为 1 1 1 、女性设为 − 1 -1 −1 ,只需控制后缀和一直小于 2 2 2(也就是充要条件)。
改变序列
你至少得发现这一点:同一种性别之间的内部顺序不发生变化。因为不愉快度就是逆序对,将其交换,逆序对只会更少,并且仍然是合法的队列。
一定要注意,我们要最小化的是不愉快度的最大值,而非其总和。
假设当前的序列不能完成任务,那么我们就要试着去调整它。考虑每次调整一个男性的位置。
我们实行如下策略:找到最后一名男性,将其往前移动,直到遇到另一名男性。然后这一名男性继续前移……直到某一名男性走到队伍的最前。
显然,男性之间的相对位置不会改变,女性也只会被某一个原本在后方的男性超过。所以此时的不愉快度的最大值恰好增大 1 1 1 (这里的增量是不可避免的:只消考虑一下最前方的女性即可)。
发现这样的操作相当于将最后方的 M M M 移动到了队列最前方,后缀和(除了队头和最后方的一段连续的 F F F )整体减小一: c n t M cnt_M cntM 减少了一嘛。
最后一段连续的 F F F 没必要特殊考虑:我不信一个负数会大于等于 2 2 2 ;队头也无需特殊考虑:如果队头都不满足条件,就可以直接说拜拜了(所以说要记得特判)。
所以我们到底要调整多少个男性呢?其实就是 ( max s u m ) − 1 (\max sum)-1 (maxsum)−1 呗——把 ( max s u m ) (\max sum) (maxsum) 调整为 1 1 1 就行。
当然咯,答案至少是 0 0 0 嘛(不然操作 − 1 -1 −1 次是什么东西?时光倒流?)。然后就没了。
代码
#include <cstdio>
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
inline long long readint(){
long long a = 0; char c = getchar(), f = 1;
for(; c<'0' or c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c and c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
inline void writeint(long long x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) writeint(x/10);
putchar(x%10+'0');
}
const int MaxN = 100005;
long long k[MaxN], n, sum[MaxN], maxSuf[MaxN], m;
char s[MaxN<<1];
int main(){
n = readint(), m = readint();
for(int i=1; i<=m; ++i){
scanf("%s",s), k[i] = readint();
for(int len=strlen(s),j=len-1; ~j; --j){
int val = (s[j] == 'F' ? -1 : 1);
sum[i] += val; // [j,len)的后缀和
maxSuf[i] = max(maxSuf[i],sum[i]);
}
}
long long ans = 1, suf = 0;
for(int i=m; i; --i){
if(sum[i] > 0) // 把k次全部累计进去
ans = max(ans,suf+(k[i]-1)*sum[i]+maxSuf[i]);
else // 只留下最后一个的后缀
ans = max(ans,suf+maxSuf[i]);
suf += sum[i]*k[i]; // 后方所有的后缀之和
}
if(suf >= 2) // 总和都不满足
puts("-1");
else // 输出的是 max 后缀和 - 1 的值
writeint(ans-1), putchar('\n');
return 0;
}