【考題 題解】 數字遊戲
題目描述
HL 中學茶餘飯後喜歡玩遊戲,一個遊戲規則如下:
共兩人蔘加遊戲,若第一個人當前手中的數為 w1,則下一秒他手上的數將會變成(x1w1 + y1) mod m;若第二個人當前手中的數為 w2,則下一秒他手上的數將會變為(x2w2 + y2) mod m。a mod b 表示 a 除以 b 的餘數。
第 0 秒,兩個人手上的數分別為 h1, h2; 請求出最快在第幾秒,第一個人手上的數為 a1 並且同時第二個人手上的數為 a2。若不可能,則輸出-1。
輸入格式
輸入包含 5*T+1 行。
第一行為一個正整數 T,表示資料組數。
對於接下來的每一組資料,第一行為一個正整數 m,第二行包括兩個整數h1, a1,第三行包括兩個整數 x1, y1,第四行包括兩個整數 h2, a2,第五行包括兩個整數 x2, y2。
輸出格式
輸出包含 T 行。
對於每一組資料,輸出一行,一個整數,如題所述。
樣例資料
input
2
5
4 2
1 1
0 1
2 3
1023
1 2
1 0
1 2
1 1
output
3
-1
資料規模與約定
對於 30%的資料:m<=1000
對於 100%的資料:T<=5, h1≠a1 且 h2≠a2,2<=m<=10^6,0<=h1,a2,x1,y1,h2,a2,x2,y2
時間限制:
1s1s
空間限制:
256MB
數字遊戲具體思路
顯然,如果使用暴力則不斷列舉即可。但是顯然會超市,我們必須要用數學方法去解決這一個問題。
至於如何數學,我們不妨舉一個例子來進行理解。
例如有這麼一串數字:
相信你一定可以看出,出現了不斷的迴圈。這是為什麼?因為每做一次運算都必須要對一個數 取模,並且當再次出現和原來相同的數字的時候便會有重複的個數,因此必然會出現迴圈。而且如果去模擬,迴圈會消耗大量的時間複雜度,我們同樣可以在時間上尋求優化的辦法。
我們知道,出現重複,會有三種情況
兩者都在兩者進入迴圈之前提前遇到
兩者都在迴圈裡遇到
一開始就相同
如果不會遇到,則同樣有三種情況
一個數字根本不存在
在兩者都進入迴圈節之前沒有遇到且兩者的數字都在迴圈節之前
當
全都不滿足時
對於在其他情況,我們只需要進行特殊判斷。
對於都出現在迴圈節內的情況,則需要用數序方法。
我們設 為進入迴圈節前的長度, 表示第一次出現 的位置, 為迴圈節的長度。 , , 相同。再設 為第一個數列進入迴圈節的圈數, 同 。因此我們只需要滿足下列等式:
輸出最小的數值即可。
程式碼如下:
#include<bits/stdc++.h>
using namespace std;
#define x1 X1
#define x2 X2
#define y1 Y1
#define y2 Y2
long long tag1[1200000]={},tag2[1200000]={};
long long m,h1,a1,x1,y1,h2,a2,x2,y2;
void Readit()
{
cin>>m>>h1>>a1>>x1>>y1>>h2>>a2>>x2>>Y2;
}
void Work()
{
memset(tag1,0,sizeof(tag1));
memset(tag2,0,sizeof(tag2));
Readit();
long long w1=h1,w2=h2;
if (h1==a1 && h2==a2) { cout<<"0\n"; return; }
long long len1=0,len2=0,step1=0,step2=0,first1=0,first2=0;
for (register long long i=1;;++i)
{
w1=(w1*x1+y1)%m;
w2=(w2*x2+y2)%m;//數值更新
if (!tag1[w1]) tag1[w1]=i;
else if (!len1) len1=i-tag1[w1],step1=tag1[w1]-1;
if (!tag2[w2]) tag2[w2]=i;
else if (!len2) len2=i-tag2[w2],step2=tag2[w2]-1;//標記陣列
if (w1==a1 && w2==a2) { cout<<i<<"\n"; return; }//如果剛好相等
if (w1==a1) first1=i;
if (w2==a2) first2=i;//判斷第一次出現在數列中的位置
if (len1 && len2) break;
}
if (first1<=step1 || first2<=step2 || !first1 || !first2) { cout<<"-1\n"; return; }
//如果第一次出現的位置是不在迴圈內的&根本沒有出現過//
for (int k1=0;k1<=1000000;k1++)
{
long long num=len1*k1+first1-first2;
long long k2=num/len2;
if (k1*len1+first1==k2*len2+first2)
{
cout<<k1*len1+first1<<"\n";
return;
}
}
cout<<"-1\n";
return;
}
int main()
{
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
long long T;cin>>T;
while (T--) Work();
fclose(stdin);fclose(stdout);
return 0;
}
總結
1.開long long
2.避免陣列開小
3.做列舉的時候確定列舉的範圍不要過小