您的位置:首页 > 其它

[BZOJ2326][HNOI2011]数学作业-矩阵乘法

2017-09-19 00:36 399 查看
数学作业

Description



突然发现矩阵乘法写得不熟……

(本zz重载运算符不打return调试整整30min)

发现一个很简单的递推式:

f[n]=f[n−1]∗(floor(lg10)+1)+n

那么发现这个floor(lg10)+1显然会有很长一段相同,所以这个值可以分块。

那么考虑按floor(lg10)的值分块,形如10..999,100…999,相同的无需重复计算。

考虑使用矩阵乘法优化这个过程:

令floor(lg10)+1=x

那么对于每一块可以构造如下转移矩阵:

x 0 0
1 1 0
0 1 1


直接分块矩阵快速幂即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>

using namespace std;

typedef long long ll;
const int N=5;

ll n,md;

struct matrix
{
ll a

,lena,lenb;

void e()
{
for(int i=0;i<lena;i++)
a[i][i]=1;
}

matrix(int _a,int _b,ll f=-1)
{
lena=_a;
lenb=_b;
memset(a,0,sizeof(a));
if(f!=-1)
{
e();
a[0][0]=f;
a[1][0]=a[2][1]=1;
}
}

matrix operator * (matrix o)
{
matrix res(lena,o.lenb);

for(int i=0;i<lena;i++)
for(int j=0;j<o.lenb;j++)
for(int k=0;k<lenb;k++)
(res.a[i][j]+=a[i][k]*o.a[k][j]%md)%=md;

return res;
}

void out()
{
for(int i=0;i<lena;i++,puts(""))
for(int j=0;j<lenb;j++)
printf("%lld ",a[i][j]);
}
};

inline matrix qpow(matrix a,ll b)
{
matrix ret(3,3);
ret.e();
while(b)
{
if(b&1)
ret=ret*a;
a=a*a;
b>>=1;
}
return ret;
}

int main()
{
scanf("%lld%lld",&n,&md);
matrix ans(3,3);
ans.e();
ll i;
for(i=10;i<=n;i=i*10)
ans=ans*qpow(matrix(3,3,i%md),i-i/10);
ans=ans*qpow(matrix(3,3,i%md),n-i/10+1);
printf("%lld\n",(ans.a[1][0]+ans.a[2][0])%md);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: