洛谷3216 HNOI2011 數學作業(矩乘優化遞推)
阿新 • • 發佈:2018-12-23
首先我們考慮,正常的 複雜度的計算應該如何計算
我們令 表示用 這些數能拼出來的數是多少
那麼
QWQ我們考慮怎麼優化這個過程。
由於不同的位數計算的時候差異很大,而且我們並沒有一個好的方法將位數不同的數放到一起計算。
那我們考慮單獨計算每一位數的貢獻。
由於是從 轉移到 很容易想到矩陣乘法
考慮到轉移的時候還涉及到
的因素
那我們發生的兩個矩陣分別是
{f[i-1],i-1,1} -> {f[i],i,1}
然後中間的轉移矩陣是
10^x 0 0
1 1 0
1 1 1
qwq感覺細節還是很多的。
qwq就是邊界的問題 嗯
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk make_pair
#define ll long long
#define int long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 10;
int mod;
int n;
int power[101010];
struct Ju{
int x,y;
int a[maxn][maxn];
Ju operator *(Ju b)
{
Ju ans;
ans.x=x;
ans.y=b.y;
memset(ans.a,0,sizeof(ans.a));
for (int i=1;i<=ans.x;i++)
for (int j=1;j<=ans.y;j++)
for (int k=1;k<=y;k++)
ans.a[i][j]=(ans.a[i][j]+a[i][k]%mod*b.a[k][j]%mod)%mod;
return ans;
}
};
Ju ans;
Ju qsm(Ju i,int j)
{
ans.x=i.x;
ans.y=i.y;
memset(ans.a,0,sizeof(ans.a));
for (int p=1;p<=i.x;p++) ans.a[p][p]=1;
while (j)
{
if (j&1) ans=ans*i;
i=i*i;
j>>=1;
}
return ans;
}
Ju a;
Ju b;
void solve(int pre,int now,int lim)
{
memset(a.a,0,sizeof(a.a));
memset(b.a,0,sizeof(b.a));
a.x=1;a.y=3;
a.a[1][1]=pre%mod;
a.a[1][2]=(power[now]-1+mod)%mod;
a.a[1][3]=1;
b.x=3;
b.y=3;
b.a[1][1]=power[now+1]%mod;
b.a[2][1]=1;
b.a[2][2]=1;
b.a[3][1]=1;
b.a[3][2]=1;
b.a[3][3]=1;
Ju tmp=qsm(b,lim-power[now]+1);
a=a*tmp;
}
signed main()
{
n=read(),mod=read();
power[0]=1;
for (int i=1;i<=19;i++) power[i]=power[i-1]*10;
int pre=0;
for (int i=0;i<=18;i++)
{
if (power[i]>n) break;
solve(pre,i,min(power[i+1]-1,n));
pre=a.a[1][1];
}
cout<<pre;
return 0;
}