BZOJ 2326[HNOI2011]数学作业(矩阵快速幂)

BZOJ 2326[HNOI2011]数学作业(矩阵快速幂)_第1张图片

解题思路:

    根据位数来进行递推,用矩阵来优化。 原因:看到题目的n>1000000000 时基本就要想到优化,而常见优化是二分,矩阵,倍增。对于递推用矩阵而距离用倍增。

 

     构造矩阵是难点:

          首先只看1,对于1,当后面若增加2-9的一个数那么相当于f[i]=f[1]*10+i;同理其他也类似,我们可以算出每个数对1这个点可以把它向前推得位数。 又注意到这是个连续递增的数列,所以可以构造一个矩阵[ f[i] ]

                                                                                                                                                   [ a[i]+1 ]

                                                                                                                                                   [ 1 ];       

又要满足递推式,那么就要设法构造另一个矩阵,构造另一个矩阵要注意要遵循和上一个模式相同。所以就可以确定另一个矩阵的长宽3*3;再去尝试一下,求出矩阵[share[i+1],1,0] 注意:此处的share数组储存的就是它的贡献,也

                                                                             [0,1,1]

                                                                             [0,0,1];

就是10的位数次方。再就是普通的矩阵快速幂


#include<iostream>
#include<stdio.h>
#include<string.h>
#include<cstdlib>
using namespace std;
long long n,m;
struct ss
 {
  long long messi[4][4];
 }q[100];
long long end[5];
long long tail=0;


ss change(ss a,ss b)
 {
  ss c;
  memset(c.messi,0,sizeof(c.messi));
  for (int i=1;i<=3;++i)
  for (int j=1;j<=3;++j)
   for (int z=1;z<=3;++z)
    c.messi[i][j]=(c.messi[i][j]+((a.messi[i][z]%m)*(b.messi[z][j]%m))%m)%m;
  return c;
 }


ss work(long long a,long long b)
 {
    ss c;
  ss ans; memset(ans.messi,0,sizeof(ans.messi));
ans.messi[1][1]=b; ans.messi[1][2]=1; ans.messi[2][2]=1; ans.messi[2][3]=1; ans.messi[3][3]=1;
bool mg=true;
while (a>0)
{
if (a%2==1)
{
if (mg)
{
for (int i=1;i<=3;++i)
for (int j=1;j<=3;++j)
 c.messi[i][j]=ans.messi[i][j]%m;
    }else 
      c=change(ans,c);
   mg=false;
 }
ans=change(ans,ans);
a=a/2;
}
return c;
 }


int main()
{
scanf("%lld %lld",&n,&m);
if (n==1)
{
cout<<1%m;
exit(0);
}
end[1]=1%m; end[2]=2; end[3]=1;
long long ci=n-1;
long long xian=9; long long ge=8; long long share=10; 
while(n>=xian)
{
++tail;
q[tail]=work(ge,share);
share=(share*10)%m;
if (ge==8) ++ge;
ge=ge*10;
xian=xian*10+9;
}
if (n>(xian-9)/10)
{
  ++tail;
  if (ge>8) q[tail]=work(n-(xian-9)/10,share);else
    q[tail]=work(n-(xian-9)/10-1,share);
     }
for (int i=tail;i>=2;--i)
{
q[i-1]=change(q[i],q[i-1]);
}
long long sug=((((q[1].messi[1][1]%m)*(end[1]%m))%m+((q[1].messi[1][2]%m)*(end[2]%m))%m)+((q[1].messi[1][3]%m)*(end[3]%m)%m))%m;
cout<<sug;
}




你可能感兴趣的:(BZOJ 2326[HNOI2011]数学作业(矩阵快速幂))