ural 1013. K-based Numbers. Version 3

       在這道題的版本三改了之後難度確實提高了不少,但是這道題也確實很有代表性。第一個版本是直接算,第二個版本是高精度,這個版本是10^18的量級,不可能直接算,需要使用矩陣乘法並用矩陣快速冪降低計算次數至logN量級。

    動歸的狀態轉移和前兩個版本一樣就不提了,主要用到的是矩陣快速冪以及邊算邊取模保證不爆long long,至於爲什麼可以邊算邊取模,不清楚的朋友可以看下數論相關的內容。矩陣的方法是從斐波那契數列的一種求法得到的啓示。有興趣的朋友可以看看。

http://www.cnblogs.com/xudong-bupt/archive/2013/03/19/2966954.html

    狀態轉移方程dp[i]=(K-1)(dp[i-1]+dp[i-2]),表示首尾均不爲0的合法數字個數,那麼N位合法數字就有dp[N]+dp[N-1](dp[N-1]是最後一位爲0的N位合法數字的個數),那麼有初始條件dp[0]=0,dp[1]=K-1,。把狀態轉移方程寫成矩陣形式


    這樣問題就變成了求這個2*2矩陣的N-1次冪,使用矩陣快速冪就可以把乘法計算次數縮減到log N的級別。

記矩陣爲A,則有以下關係


    10^18不超過2^70,那麼我們需要做的就是先求出n<70的70個矩陣並用dp[i]保存起來備用。

然後把N-1轉成二進制形式來決定最後的矩陣由哪些矩陣相乘得到,比如N-1爲6的話,二進制是110,則



    最後剩下的問題就是如何進行矩陣計算,這裏我定義矩陣相乘和數字相乘的兩個函數,返回的是取模後的矩陣和數字。矩陣相乘就是把兩個矩陣的對應項按矩陣乘法的規則進行就行了,難點在於怎麼計算兩個大數的乘積。也就是最後需要解決的問題——定義兩個數字相乘的函數mulNum()。

    思路是利用取模的性質,把數字拆開乘,邊算邊取模,一個i位數乘以一個j位數就需要做i*j次乘法。舉個例子,234*567,寫成(2*100+3*10+4*1)*(5*100+6*10+7*1)

在這裏可以先算出10的各次冪取模後的值,因爲會重複用到,用tens數組保存,計算到40次冪就夠了。在這裏需要注意一個小細節,(10^18-1)乘以10的話會爆long long,乘以個位數不會,所以每次用dp[i]算dp[i+1]時就乘以5取模後再乘以2取模。在進行對應位乘法時也一樣,比如上例的2*100*5*100,就先算2*dp[4],取模後在乘5,再取模。

    最後貼上代碼

#include <iostream>
#include <string>
#include <vector>
using namespace std;
vector<long long> temp(2, 0);
vector<vector<long long> >v(2, temp);
vector<vector<long long> >dp[71];
long long N, K, M;
long long tens[40];
long long mulNum(long long x,long long y)
{
	long long ans = 0;
	string strx,stry;
	while (x > 0)
	{
		strx = strx + char((x % 10) + '0');
		x = x / 10;
	}
	while (y > 0)
	{
		stry = stry + char((y % 10) + '0');
		y = y / 10;
	}
	for (int i = 0;i < strx.length();i++)
	{
		for (int j = 0;j < stry.length();j++)
		{
			ans = (ans + ((((strx[i] - '0')*tens[i + j]) % M)*(stry[j] - '0')) % M) % M;
		}
	}
	return ans;
}
vector<vector<long long> >mulMat(vector<vector<long long> >x, vector<vector<long long> >y)
{
	vector<vector<long long> > tmp = v;
	tmp[0][0] = (mulNum(x[0][0], y[0][0]) + mulNum(x[0][1], y[1][0])) % M;
	tmp[0][1] = (mulNum(x[0][0], y[0][1]) + mulNum(x[0][1], y[1][1])) % M;
	tmp[1][0] = (mulNum(x[1][0], y[0][0]) + mulNum(x[1][1], y[1][0])) % M;
	tmp[1][1] = (mulNum(x[1][0], y[0][1]) + mulNum(x[1][1], y[1][1])) % M;
	return tmp;
}
void ini()
{
	tens[0] = 1;
	for (int i = 1;i <= 36;i++)
		tens[i] = (((tens[i - 1] * 5) % M) * 2) % M;

	dp[0] = v;
	dp[0][0][0] = dp[0][0][1] = K - 1;
	dp[0][1][0] = 1;dp[0][1][1] = 0;
	for (int i = 1;i < 70;i++)
		dp[i] = mulMat(dp[i - 1], dp[i - 1]);
}
void getA()
{
	vector<vector<long long> > t(2,temp);
	t[0][0] = t[1][1] = 1;
	t[0][1] = t[1][0] = 0;
	long long x = N - 1;
	string str;
	while (x != 0)
	{
		str = str + char(x % 2 + '0');
		x = x / 2;
	}
	for (int i = 0;i < str.length();i++)
	{
		if (str[i] == '1')
			t = mulMat(t, dp[i]);
	}
	long long ans = mulNum(t[0][0], K - 1);
	ans = (ans + mulNum(t[1][0], K - 1)) % M;
	cout << ans << endl;
}
int main()
{
	cin >> N >> K >> M;
	if (N == 1)
	{
		cout << (K - 1) % M << endl;
		return 0;
	}
	ini();
	getA();
}


發佈了35 篇原創文章 · 獲贊 19 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章