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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章