10.29T1 倍增+同余

6037 -- 【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

Source

root
 
 
 
此题难度很大

容易发现每个人都是独立的,那么我们只需要求出每个人在什么时候会没钱,最早没钱的那个人就是使得大家离开的那个人。如果大家都不会没钱那么就不会离开。

对于每个人,用类似 st 表的方式计算出每个人在经过2i轮之后硬币的变化,就可以轻松的算出这个人离开的时间。

时间复杂度 O(NlogN)

实际上要维护三个倍增数组代表最大的支付前缀,到的位置,还有支付和

code:

  1 #include<iostream>
  2 #include<cstdio>
  3 #define N 1000005
  4 using namespace std;
  5 long long n;
  6 long long m;
  7 long long NXT[N][21],PAY[N][21],DEL[N][21];
  8 void initalize() {
  9     for(long long i=1; i<=20; i++) {
 10         for(long long j=0; j<n; j++) {
 11             NXT[j][i]=NXT[NXT[j][i-1]][i-1];
 12             DEL[j][i]=DEL[j][i-1]+DEL[NXT[j][i-1]][i-1];
 13             PAY[j][i]=max(PAY[j][i-1],DEL[j][i-1]+PAY[NXT[j][i-1]][i-1]);
 14         }
 15     }
 16 }
 17 bool complete_round(long long step,long long &now,long long &money) {
 18     for(long long i=20; i>=0; i--) {
 19         if(step&(1<<i)) {
 20             if(money>PAY[now][i]) {
 21                 money-=DEL[now][i];
 22                 now=NXT[now][i];
 23             } else return false;
 24         }
 25     }
 26     return true;
 27 }
 28 long long rest_walk(long long now,long long money) {
 29     long long footstep=0;
 30     for(long long i=20; i>=0; i--) {
 31         if(money>PAY[now][i]) {
 32             footstep+=(1<<i)*n;
 33             money-=DEL[now][i];
 34             now=NXT[now][i];
 35         }
 36     }
 37     return footstep;
 38 }
 39 long long a[N],ans[N],isinf[N],min0=99999999999999999,bcc,siz[N],win[N],belong[N];
 40 long long read(){
 41     long long x=0,f=1;
 42     char c=getchar();
 43     while(!isdigit(c)){
 44         if(c=='-')f=-1;
 45         c=getchar();
 46     }
 47     while(isdigit(c)){
 48         x=(x<<3)+(x<<1)+c-'0';
 49         c=getchar();
 50     }
 51     return x*f;
 52 }
 53 int main() {
 54 //    freopen("game7.in","r",stdin);
 55     n=read();
 56     for(long long i=0; i<n; i++) {
 57         a[i]=read();
 58     }
 59     m=read();
 60     for(long long i=0; i<m; i++) {
 61         char c=getchar();
 62         while(c!='W'&&c!='P')c=getchar();
 63         if(c=='W')win[i]=-1;
 64         else win[i]=1;
 65     }
 66     for(long long i=0; i<m; i++) {
 67         if(belong[i])continue;
 68         long long now=i;
 69         bcc++;
 70         while(1) {
 71             siz[bcc]++;
 72             belong[now]=bcc;
 73             NXT[now][0]=(now+n)%m;
 74             PAY[now][0]=win[now];
 75             DEL[now][0]=win[now];
 76             now=NXT[now][0];
 77             if(belong[now])break;
 78         }
 79     }
 80     initalize();
 81     for(long long i=0; i<n; i++) {
 82         long long money=a[i],now=i%m;
 83         ans[i]=(i+1);
 84         if(complete_round(siz[belong[now]],now,money)) {
 85             long long delta=a[i]-money;
 86             if(delta<=0) {
 87                 isinf[i]=1;
 88                 continue;
 89             }
 90             ans[i]+=(a[i]/delta)*siz[belong[now]]*n-n;
 91             a[i]%=delta;
 92             ans[i]+=rest_walk(NXT[i%m][0],a[i]);
 93         }
 94         else ans[i]+=rest_walk(i%m,a[i]);
 95     }
 96     long long flag=0;
 97     for(long long i=0; i<n; i++) {
 98         if(!isinf[i]) {
 99             min0=min(min0,ans[i]);
100             flag=1;
101         }
102     }
103     if(flag)cout<<min0;
104     else puts("-1");
105     return 0;
106 }

over

转载于:https://www.cnblogs.com/saionjisekai/p/9872580.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值