1. 程式人生 > >51nod 1126 求遞推序列的第N項

51nod 1126 求遞推序列的第N項

這道題是好幾個月前周賽做的,當時敲了很久的矩陣快速冪,結果打完別人告訴我可以用打表做……

因為每一項都只由前兩項決定,所以我們不妨用(f[n-2],f[n-1])來表示f[n]。如果(f[n-2],f[n-1])這個狀態在之前出現過那麼後面一定就會重複前面的迴圈。而前兩項只有7*7=49種,所以最多50項開始就會出現迴圈,並且週期的上界是49。

但是網上能搜到的打表題解基本都是錯的,我當時找了不少都能被hack掉,因為這道題資料實在是太水了……我打表的做法改了很多次,每次都能AC但是對拍還是會拍出錯誤。

網上打表的做法主要錯誤有兩個:

1迴圈到49項就退出迴圈。好吧其實我根本不知道為什麼他們要在49之後退出迴圈。(f[n-2],f[n-1])最多有49種可能,但這並不代表到了49沒有找到週期週期就是49。

2找到週期t後認為答案是f[n%t]。這也是我犯過的錯誤。數列有周期並不代表第一項一定在週期裡,可能是從某一項開始才開始迴圈。比如對於某組a,b可能x->(y->z->y->z...)

我的做法是用一個used陣列來記錄出現過的(f[n-2],f[n-1]),並且用一個變數start來記錄迴圈開始的位置,最後的答案就是f[(n-start)%t + start],這個做法對拍了很久應該是沒有問題的了。

#include<bits/stdc++.h>
using namespace std;

int main()
{
	int used[7][7];
	int n,a,b;
	cin >> a >> b >> n;
	if(n==1||n==2){
		cout << 1 << endl;
		return 0;
	}
	int f[100],t,start;
	memset(used,0,sizeof(used));
	f[1]=f[2] = 1;
	for(int i = 3;;i++)
	{
		f[i] = (a*f[i-1]+b*f[i-2])%7;
		f[i] = (f[i]+7)%7;
		if(!used[f[i]][f[i-1]])used[f[i]][f[i-1]] = i;
		else {
			t = i - used[f[i]][f[i-1]];
			start = used[f[i]][f[i-1]];
			break;
		}
	}
	cout << f[(n-start)%t + start] << endl;
}