[洛谷P3615]如厕计划

89 篇文章 0 订阅

题目

传送门 to luogu

思路

感谢@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,x1 —— 结论一 中已经说了,男多女少,是一定无法完成任务的。

结论三

如果不存在一个后缀,满足男性的数量 = = = 女性的数量 + 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 <xy (即队头的值); F F F 的离开又让 c n t F cnt_F cntF 减小了 1 1 1 ,所以差增大了一。最终,这个差 ≤ x − y < 2 \le x-y<2 xy<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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值