CodeForces 548C - Mike and Frog(思维+暴力)

题目链接 https://cn.vjudge.net/problem/CodeForces-548C

【题意】
给定9个正整数 m, h1, a1, x1, y1, h2, a2, x2, y2 有两种植物,其中h1和h2分别是它们的初始高度,每经过一个单位时间,它们的高度就会变成h’=(x*h+y)%m, 两种植物都从时刻0开始计时,现在问你是否存在这样一个时刻t,使得在时刻t,第一株植物的高度为a1同时第二株植物的高度恰好是a2,输出最小的t,如果t不存在,输出-1(0<=所有变量<=1e6,h1!=a1,h2!=a2)

【思路】
      一开始就直接按照题意暴力做的,循环1e6次,如果还没法到达目标状态就输出-1,然而WA了,然后就不太会做了,看网上的题解好像都是卡住2*m的范围求循环节,为什么是2*m,我也没太想清楚。然后我就用了下面的这另一种方法做了,
      首先要注意的是,因为有mod m,所以肯定会有循环出现,但是这不是一个简单的“环”,或者说不是所有能变换到的数字都在循环里,有些只会出现一次就再也不会出现了,举个最简单的例子来说,如果m=10,x=2,y=0,然后h从1开始,经过不断的计算,我们可以得到一系列h的值为1,2,4,8,6(16mod10),2(12mod10),好了2重复出现过,这时已经找到了循环节,但是注意一开始的那个1在以后的变换中再也不会出现了。
      在解决这道题的时候,代码里用fir变量记录目标高度第一次出现的时间,然后len记录循环节长度,都初始化为-1,这个len比较特殊,如果目标高度不在环里,那么len就还是-1表示目标高度只会出现一次。
      如果说这两种植物的目标高度有一个压根就没出现过,那肯定无解;
      否则的话,我们还要去判断这两个目标高度是处在“环”中呢,还是说出现一次就再也不会出现了,如果两个目标高度都不在“环”中,都只出现一次的话,那么如果它们出现的时间fir相同,说明fir就是唯一的解,否则无解;
      如果一个在“环”中,另一个只出现一次,那么有解的条件就必须是fir[只出现一次]>=fir[在环中] 同时 fir[只出现一次]=fir[在环中]+k*len[环] k是任意非负整数,即只出现一次的fir时间要晚于在“环”中的fir,同时它们的时间差还正好是循环节的整数倍才行
      如果都在“环”中的话,那满足要求时一定有fir[0]+k1×len[0]=fir[1]+k2×len[1](k1,k2为任意非负整数),求一个满足等号成立的最小值就行,感觉能用数学公式推出来但太菜了不会,就直接暴力往上加了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxm=1000050;

ll mod;
ll h[2],a[2],x[2],y[2];
ll fir[2];//fir记录第一次到达目标状态的时刻,无法到达则为-1
ll len[2];//len记录循环节长度,如果目标状态不在循环节中则为-1
bool used[maxm];

void calc(){
    for(int k=0;k<2;++k){
        //计算fir
        fir[k]=-1;
        memset(used,0,sizeof(used));
        ll cur=h[k],cnt=0;
        while(1){
            if(used[cur]) break;
            if(cur==a[k]) { fir[k]=cnt; break; }
            ++cnt;
            used[cur]=true;
            cur=(cur*x[k]+y[k])%mod;
        }

        //从目标状态出发计算len
        len[k]=-1;
        memset(used,0,sizeof(used));
        cur=a[k],cnt=0;
        while(1){
            if(used[cur]){
                if(cur==a[k]) len[k]=cnt;
                break;
            }
            ++cnt;
            used[cur]=true;
            cur=(cur*x[k]+y[k])%mod;
        }
    }
}

int main(){
    scanf("%lld",&mod);
    for(int k=0;k<2;++k){
        scanf("%lld%lld",&h[k],&a[k]);
        scanf("%lld%lld",&x[k],&y[k]);
    }
    calc();

    /*for(int k=0;k<2;++k){
        printf("首次出现时间:%d 循环节:%d\n",fir[k],len[k]);
    }*/

    if(fir[0]==-1 || fir[1]==-1) puts("-1");//根本就没有目标状态
    else if(len[0]==-1 && len[1]==-1){//都不在循环节里面,都只会出现一次
        if(fir[0]==fir[1]) printf("%lld\n",fir[0]);
        else puts("-1");
    }
    else if(len[0]==-1 && len[1]!=-1){//0号输入的目标状态只出现一次
        if(fir[0]>=fir[1] && (fir[0]-fir[1])%len[1]==0) printf("%lld\n",fir[0]);//时间差刚好是循环的整数倍才行
        else puts("-1");
    }
    else if(len[0]!=-1 && len[1]==-1){
        if(fir[1]>=fir[0] && (fir[1]-fir[0])%len[0]==0) printf("%lld\n",fir[1]);
        else puts("-1");
    }
    else{//目标状态都处在循环节中,fir[0]+m*len[0]=fir[1]+n*len[1](m,n为任意非负整数) 那就暴力往上加吧,不会用数学式子推
        ll ans=-1;
        for(ll i=0;i<1e6;++i){
            ll tmp=fir[0]+i*len[0]-fir[1];
            if(tmp>=0 && tmp%len[1]==0) { ans=tmp+fir[1]; break; }
        }
        printf("%lld\n",ans);
    }
    return 0;
}
posted @ 2018-04-17 22:57  不想吃WA的咸鱼  阅读(145)  评论(0编辑  收藏  举报