第i个人抽到的序列与(i%m)是一样的,所以只需要讨论i%m剩余系下的问题。
首先搞出来每个人抽牌的循环,然后我们要判定这个人需要多少个循环加多少轮才能没钱。
需要知道两个东西,一个循环中最小的前缀和和一个循环的和。这样循环数就大约是(初始钱数-最小前缀和-1)/一个循环的和+1,还需要的轮数就是跑完循环之后第一次到0的轮数。
现在考虑维护方法:(我比较弱,这些想了好久)
先复制一次,循环的和显然,最小前缀和用单调队列。还需要的轮数我们从后往前扫整个序列,维护每个数最前面出现的位置,然后查询就可以了。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
#define inf 2000000000000000000ll
#define eps 1e-10
#define md
#define N 4000010
using namespace std;
const int D=2000000;
struct QQ { int dt,id;} q[N];
int a[N],b[N],r[N],sum[N],mn[N],T[N];
char st[N];
bool vis[N];
ll lun[N],ed[N],win[N],last[N];
int main()
{
#ifndef ONLINE_JUDGE
freopen("data.in","r",stdin); freopen("data.out","w",stdout);
#endif
int n,m;
scanf("%d",&n);
for (int i=0;i<n;i++) scanf("%d",&a[i]);
scanf("%d",&m); scanf("%s",st);
for (int i=0;i<m;i++) b[i]=st[i]=='W'?1:-1;
for (int i=0;i<m;i++)
if (!vis[i])
{
int x=i,tot=0;
while (!vis[x])
{
vis[x]=1; r[++tot]=x; sum[tot]=sum[tot-1]+b[x];
x=(x+n)%m;
}
for (int j=1;j<=tot;j++)
{
r[j+tot]=r[tot];
sum[j+tot]=sum[j+tot-1]+b[r[j]];
}
int h=1,w=0;
for (int j=1;j<=tot;j++)
{
while (h<=w&&q[w].dt>=sum[j]) w--;
q[++w].id=j; q[w].dt=sum[j];
}
for (int j=1;j<=tot;j++)
{
while (h<=w&&q[h].id<j) h++;
mn[j]=-(q[h].dt-sum[j-1]);
win[j]=sum[j+tot-1]-sum[j-1];
while (h<=w&&q[w].dt>=sum[j+tot]) w--;
q[++w].id=j+tot; q[w].dt=sum[j+tot];
}
for (int j=D-(tot<<1);j<=D+(tot<<1);j++) last[j]=inf;
for (int j=(tot<<1);j>tot;j--)
last[sum[j]+D]=j;
for (int j=tot;j;j--)
{
x=r[j]; T[x]=tot;
last[sum[j]+D]=j;
while (x<n)
{
if (win[j]>=0&&a[x]>mn[j]) lun[x]=inf;
else
{
if (a[x]<=mn[j]) lun[x]=0;
else lun[x]=(a[x]-mn[j]-1)/(-win[j])+1;
ed[x]=a[x]+lun[x]*win[j];
ed[x]=last[-ed[x]+sum[j-1]+D]-(j-1);
}
x+=m;
}
}
}
ll ans=inf;
for (int i=0;i<n;i++)
{
if (lun[i]==inf) continue;
ans=min(ans,(lun[i]*T[i]+ed[i]-1)*n+i+1);
}
if (ans==inf) printf("-1\n");
else printf("%lld\n",ans);
return 0;
}