神仙DP
注意到\(N \leq 10^{18}\),不能夠直接數位DP,於是考慮形成的\(N\)位數的性質。
因為低位一定不會比高位小,所以所有滿足條件的\(N\)位數一定是不超過\(9\)個\(f(x)(x \in [1,N])\)的和,其中\(f(x) = \sum\limits_{i=0}^{x-1} 10^i\),且其中一定有一個\(f(N)\)。
考慮由\(f(x)\ \bmod\ P\)形成的數列,因為\(f(x) = 10f(x-1) + 1\),所以這個數列一定會存在一個不超過\(P\)的循環節。那么我們可以通過這個預處理出\(cnt_i = \sum\limits_{x=1}^N[f(x) \mod P = i]\),同時求出\(f(N)\ \bmod\ P\)的值。
接下來就可以DP了:設\(f_{i,j,k}\)表示考慮了\(cnt_0 \sim cnt_{i-1}\),選擇了\(k\)個\(f(x)\),它們的和\(\bmod\ P = j\)的方案數。轉移考慮枚舉\(cnt_i\)中選擇多少個,這就是一個插板法,轉移系數是一個組合數。
最后的答案就是\(\sum\limits_{i=0}^8 f_{P,(P - f(N))\ \bmod P,i}\),\(i\)最大為\(8\)的原因是必須要選擇一個\(f(N)\)。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int MOD = 999911659;
int dp[503][503][9] , Cnt[503] , dir[503] , N , P;
int poww(int a , int b){
int times = 1;
while(b){
if(b & 1) times = times * a % MOD;
a = a * a % MOD; b >>= 1;
}
return times;
}
int binom(int a , int b){
int times = 1;
for(int i = a ; i > a - b ; --i)
times = times * i % MOD * poww(a - i + 1 , MOD - 2) % MOD;
return times;
}
signed main(){
cin >> N >> P;
int cur = 1 % P , cnt = 1 , tmp = 1 % P , ed;
do{dir[cur] = cnt; ++cnt; cur = (cur * 10 + 1) % P;}while(!dir[cur]);
for(int i = 1 ; i < dir[cur] && i <= N ; ++i , tmp = (tmp * 10 + 1) % P) ++Cnt[ed = tmp];
if(dir[cur] <= N){
for(int i = dir[cur] ; i < cnt ; ++i , tmp = (tmp * 10 + 1) % P) Cnt[ed = tmp] = (N - dir[cur] + 1) / (cnt - dir[cur]) % MOD;
for(int i = 1 ; i <= (N - dir[cur] + 1) % (cnt - dir[cur]) ; ++i , tmp = (tmp * 10 + 1) % P) ++Cnt[ed = tmp];
}
dp[0][0][0] = 1;
for(int i = 0 ; i < P ; ++i)
for(int j = 0 ; j <= 8 ; ++j){
int val = binom(Cnt[i] + j - 1 , j);
if(!val) continue;
for(int k = 0 ; k < P ; ++k)
for(int l = 0 ; l + j <= 8 ; ++l)
dp[i + 1][(k + i * j) % P][l + j] = (dp[i + 1][(k + i * j) % P][l + j] + val * dp[i][k][l]) % MOD;
}
int sum = 0;
for(int i = 0 ; i <= 8 ; ++i)
sum = (sum + dp[P][(P - ed) % P][i]) % MOD;
cout << sum;
return 0;
}