[51nod月赛19D]石头剪刀布威力加强版

题目大意

小A和小B在玩石头剪刀布,他们每个人写出一个序列。
小A写出了n个数。
小B写出了m个数。
其中0代表石头,1代表剪刀,2代表布
0>1,1>2,2>0。
他们总共进行k轮,第一轮选择第一个数字,后面每一轮两个人都选择序列的下一个数进行比赛(序列结尾的下一个位置在序列开头)。
问小A和小B每人赢了几局。

瞎做

例如我们现在统计A赢了几局。
假如Ai和Bj打,那么下一次轮到Ai时,B的位置会往后位移n。
因此在B序列形成了若干个环。
枚举每个位置i,看看它赢了多少次,假设一共有p局。
若其对应位置形成的环长度为k,且(p-i+1)/(k*n)=l……r
那么首先有l*(Ai赢了这个环多少次)的答案贡献。
对于剩余r的部分,如果把环展开来,则对应了环上一个长度为(r-1)/n+1的循环区间,前缀和统计赢了多少次。
把上述过程做两次即可。

#include<cstdio> 
#include<algorithm>
#include<map>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=500000+10;
int next[maxn],a[maxn],b[maxn],cnt[maxn][3],len[maxn],belong[maxn],sum[maxn][3],id[maxn];
map<int,int> wz[maxn];
int i,j,k,t,wdc,mi,mx,n,m,top,tot;
ll p,ans,l,r;
int read(){
    int x=0;
    char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x;
}
void work(){
    fo(i,1,m){
        next[i]=(i+n)%m;
        if (!next[i]) next[i]=m;
    }
    top=0;
    fo(i,1,m) belong[i]=id[i]=0;
    fo(i,1,m)
        if (!belong[i]){
            ++top;
            cnt[top][0]=cnt[top][1]=cnt[top][2]=0;
            len[top]=0;
            j=i;k=0;
            while (!belong[j]){
                belong[j]=top;
                cnt[top][b[j]]++;
                id[j]=++len[top];
                wz[top][id[j]]=j;
                sum[j][0]=sum[k][0];sum[j][1]=sum[k][1];sum[j][2]=sum[k][2];
                sum[j][b[j]]++;
                k=j;
                j=next[j];
            }
        }
    j=1;
    mx=0;
    fo(i,1,n){
        if (!p) break;
        k=len[belong[j]];
        l=p/((ll)k*n);
        r=p%((ll)k*n);
        ans+=(ll)l*cnt[belong[j]][(a[i]+1)%3];
        if (r){
            t=j;
            mi=(r-1)/n+1;
            if (id[t]+mi-1<=k){
                wdc=wz[belong[j]][id[t]+mi-1];
                ans+=(ll)(sum[wdc][(a[i]+1)%3]-sum[t][(a[i]+1)%3]);
                if (b[t]==(a[i]+1)%3) ans++;
            }
            else{
                wdc=wz[belong[j]][(id[t]+mi-2)%k+1];
                ans+=(ll)sum[wdc][(a[i]+1)%3];
                wdc=wz[belong[j]][k];
                ans+=(ll)(sum[wdc][(a[i]+1)%3]-sum[t][(a[i]+1)%3]);
                if (b[t]==(a[i]+1)%3) ans++;
            }
        }
        j++;
        if (j>m) j=1;
        p--;
        mx++;
    }
    p+=mx;
    fo(i,1,top) wz[i].clear();
}
int main(){
    n=read();m=read();scanf("%lld",&p);
    fo(i,1,n) a[i]=read();
    fo(i,1,m) b[i]=read();
    work();
    printf("%lld ",ans);
    fo(i,1,max(n,m)) swap(a[i],b[i]);
    swap(n,m);
    ans=0;
    work();
    printf("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值