LOJ #2734 Luogu P3615 [JOI2016春季合宿]Toilets (结论、贪心)

题目链接

(loj) https://loj.ac/problem/2734
(luogu) https://www.luogu.org/problem/P3615

题解

嗯,考场上肝了(3h)然而最后发现一个智障地方没想到……我果然还是菜的真实啊
首先队列合法(能在(N)分钟内解决)当且仅当: 每一个长度为偶数的后缀女生数量都不少于一半(一个等价的表述是,如果把男人看成(1)女人看成(-1)那么任何一个后缀和不大于(1), 但是这里由于是对女生操作所以只考虑女生会好很多)。证明只能根据题意一步步推,这里略去。
然后考虑(O(n))做法: 若总共男人数严格多于女人数则无解,否则从后往前扫,若某个后缀女人数量少于一半了就把最近的女人移过来,显然最优。
因此,设从后往前第(i)个女人位于从后往前第(i)个位置,则答案为(max(0,max_i (b_i-2i))).
现在字符串长度很大,那么发现对于每一个重复的子串,若其中女人数量多于一半则只需考虑从后往前的第一次重复,否则只需考虑最后一次,于是直接做即可。
时间复杂度(O(sum |s_i|)).

代码

#include<bits/stdc++.h>
#define llong long long
#define mkpr make_pair
using namespace std;

const int N = 2e5;
struct Element
{
	vector<char> s;
	int cnt0,cnt1; llong t;
} a[N+3];
llong sum[N+3],sum1[N+3];
char str[N+3];
llong n; int m;

int main()
{
	scanf("%lld",&n);
	scanf("%d",&m);
	llong tot = 0ll;
	for(int i=1; i<=m; i++)
	{
		scanf("%s%lld",str+1,&a[i].t); int len = strlen(str+1);
		for(int j=1; j<=len; j++)
		{
			a[i].s.push_back(str[j]);
			if(str[j]=='F') {tot-=a[i].t; a[i].cnt1++;}
			else {tot+=a[i].t; a[i].cnt0++;}
		}
	}
	for(int i=m; i>=1; i--)
	{
		sum[i] = sum[i+1]+a[i].s.size()*a[i].t;
		sum1[i] = sum1[i+1]+a[i].cnt1*a[i].t;
	}
	if(tot>0) {puts("-1"); return 0;}
	llong ans = 0ll;
	for(int i=m; i>=1; i--)
	{
		llong cur1 = sum1[i+1],cur = sum[i+1];
		if(2*a[i].cnt1<a[i].s.size())
		{
			cur1 += a[i].cnt1*(a[i].t-1ll);
			cur += a[i].s.size()*(a[i].t-1ll);
		}
		for(int j=a[i].s.size()-1; j>=0; j--)
		{
			cur++;
			if(a[i].s[j]=='F') {cur1++;}
//			printf("cur=%lld cur1=%lld
",cur,cur1);
			ans = max(ans,cur-(cur1<<1)-1);
		}
	}
	printf("%lld
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/suncongbo/p/11746585.html