BZOJ4295[PA2015]Hazard 乱搞

Posted CQzhangyu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ4295[PA2015]Hazard 乱搞相关的知识,希望对你有一定的参考价值。

【BZOJ4295】[PA2015]Hazard

Description

有n个人在轮流玩赌博机,一开始编号为i的人有a[i]元钱。赌博机可以抽象为一个长度为m的仅包含1和-1的序列,若抽到1,那么你将得到1块钱;若抽到-1,你将输掉1块钱。
第1局,第1个人会抽到序列中的第1项;第2局,第2个人会抽到序列中的第2项;第3局,第3个人会抽到序列中的第3项......即:第i个人抽完后轮到第i+1个人去抽,特别地,第n个人抽完后轮到第1个人去抽。序列第i项被抽到之后,下一个被抽到的将会是第i+1项,特别地,序列第m项被抽到之后,下一个被抽到的将会是第1项。
如果在某一轮,有个人输光了所有的钱,那么这场赌博游戏就会结束,请求出游戏在哪一轮结束,或者判断这个游戏会永远进行下去。

Input

第一行包含一个正整数n(1<=n<=1000000),表示玩家的个数。
第二行包含n个正整数a[1],a[2],...,a[n](1<=a[i]<=1000000),依次表示每个玩家一开始持有的钱数。
第一行包含一个正整数m(1<=m<=1000000),表示序列的长度。
第四行包含一个长度为m的仅包含W和P的字符串,表示这个序列,其中W表示1,P表示-1。

Output

若游戏会永远进行下去,输出-1。否则输出游戏在哪一轮结束。

Sample Input

4
2 3 2 1
3
WPP

Sample Output

12

题解:显然我们应该找到循环节,然后将一个循环内的所有人放到一起考虑。假如我们已经拿出了某个循环中的所有人,我们先对于每个人,处理处他在转一圈时,收益的最小值(亏损的最大值);再处理出整个循环的权值和。那么如果权值和是负数,那么这个人肯定是先转若干圈,直到他的剩余钱数不足这个亏损的最大值,然后找到他第一个把钱输光的位置即可。如果权值和是正数,我们看这个人的钱数是否大于这个亏损的最大值。如果大于,则这个人永远不会输光,否则找到他第一个把钱输光的位置。

以上内容都可以通过前缀和,前缀最大值,前缀pre,后缀和,后缀最大值,后缀nxt搞定。特别地,如果n>m或m>n都要特殊考虑一下。

 

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn=1000010;
typedef long long ll;
int n,m;
ll ans;
int A[maxn],B[maxn],C[maxn],v[maxn],vis[maxn],s[maxn],s1[maxn],s2[maxn],m1[maxn],m2[maxn],p[maxn];
int memp[maxn<<1],*last=memp+maxn,mn[maxn];
char str[maxn];
inline int abs(int x) {return x>0?x:-x;}
void work(int S)
{
	int i,j,L=0,sum=0;
	for(i=S;!vis[i];i=(i+n)%m,L++)	p[L]=i,v[L]=(str[i]==‘W‘)?1:-1,sum+=v[L],vis[i]=1;
	s2[L]=m2[L]=0;
	for(i=0;i<L;i++)	s1[i]=s1[i-1]+v[i],m1[i]=min(m1[i-1],s1[i]);
	for(i=L-1;i>=0;i--)	s2[i]=s2[i+1]+v[i],m2[i]=max(m2[i+1],s2[i]),mn[i]=min(s2[i]+m1[i-1],s2[i]-m2[i+1]);
	if(sum<0)
	{
		sum=-sum;
		for(i=0;i<L;i++)
		{
			j=p[i];
			if(A[j]!=-1&&A[j]+mn[i]>0)	B[j]=(A[j]+mn[i]+sum-1)/sum,A[j]-=B[j]*sum;
		}
	}
	for(i=0;i<=L;i++)	last[i]=last[-i]=-2;
	for(i=0;i<L;i++)
	{
		j=p[i];
		if(last[s1[i-1]]==-2)	last[s1[i-1]]=i-1;
		if(A[j]!=-1&&A[j]+mn[i]<=0&&last[-s2[i]-A[j]]!=-2)
			ans=min(ans,(last[-s2[i]-A[j]]+L-i+(ll)B[j]*L)*n+C[j]);
	}
	for(i=0;i<=L;i++)	last[i]=last[-i]=-2;
	for(i=L-1;i>=0;i--)
	{
		j=p[i],last[s2[i+1]]=i+1;
		if(A[j]!=-1&&A[j]+mn[i]<=0&&last[s2[i]+A[j]]!=-2)
			ans=min(ans,(last[s2[i]+A[j]]-i-1+(ll)B[j]*L)*n+C[j]);
	}
}
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)	f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+gc-‘0‘,gc=getchar();
	return ret*f;
}
int main()
{
	//freopen("bz4295.in","r",stdin);
	n=rd();
	int i;
	for(i=0;i<n;i++)	A[i]=rd();
	m=rd();
	scanf("%s",str);
	for(i=0;i<m;i++)	C[i]=i+1;
	for(i=m;i<n;i++)	if(A[i]<A[i%m])	A[i%m]=A[i],C[i%m]=i+1;
	for(i=n;i<m;i++)	A[i]=-1;
	ans=1ll<<60;
	for(i=0;i<m;i++)	if(!vis[i])	work(i);
	if(ans==(1ll<<60))	printf("-1");
	else	printf("%lld",ans);
	return 0;
}

 

以上是关于BZOJ4295[PA2015]Hazard 乱搞的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ4293: [PA2015]Siano

BZOJ4293: [PA2015]Siano

[bzoj4292] [PA2015]Równanie

bzoj4292 PA2015 Równanie 枚举

BZOJ4293: [PA2015]Siano 线段树上二分

BZOJ 1045 糖果传递 题解 递推乱搞就对了