您的位置:首页 > 其它

ural 1013. K-based Numbers. Version 3

2016-03-24 18:32 351 查看
       在这道题的版本三改了之后难度确实提高了不少,但是这道题也确实很有代表性。第一个版本是直接算,第二个版本是高精度,这个版本是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
+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();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ural timus 1013 动态规划