题意:给你n,要求从1到n的数字拼在一起,%m,m给出,n<=1e18。
其实n这么大很容易想到矩阵乘法,问题是直接想不太容易想出来(我弱啊= =)
一般来说这种很大的题目要先想个dp(矩阵乘法)。
设f[i]为i的和,那么有:
f[i]=f[i−1]∗10+i(i<10)
f[i]=f[i−1]∗100+i(i为两位数)
….
这就很明显了啊,随便推推就出来了,注意分段。
有一个小问题就是列矩阵的时候要把i拆成(i-1)+1,这样子递推会比较方便,具体看图:
如果只是二维的话不大好处理= =
#include<cstdio>
#include<cstring>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=1e5+5;
typedef long long ll;
ll c,n,mo;
struct matrix
{
ll p[4][4];
int n,m;
matrix()
{
memset(p,0,sizeof(p));
}
}a,res;
matrix operator *(matrix a,matrix b)
{
matrix c;
c.n=a.n,c.m=a.m;
fo(i,1,a.n)
fo(j,1,b.m)
fo(k,1,a.m)
c.p[i][j]=(c.p[i][j]+1ll*a.p[i][k]*b.p[k][j])%mo;
return c;
}
inline void reset()
{
a.p[1][2]=a.p[1][3]=a.p[2][3]=0ll;
a.p[2][1]=a.p[2][2]=a.p[3][1]=a.p[3][2]=a.p[3][3]=1ll;
a.p[1][1]=c%mo;a.n=a.m=3;
res.p[1][2]=(c/10-1ll)%mo;
res.p[1][3]=1ll;
res.n=1,res.m=3;
}
inline void pow(ll b)
{
while (b)
{
if (b&1)res=res*a;
a=a*a;
b>>=1;
}
}
int main()
{
scanf("%lld%lld",&n,&mo);
res.p[1][1]=0ll,c=1ll;
fo(i,1,18)
{
c*=10ll;
reset();
ll k;
if (c<=n)k=c/10*9ll;
else k=n-c/10+1ll;
pow(k);
if (c>n)break;
}
printf("%lld\n",res.p[1][1]);
}