emm,这题和数位dp关系不大
但是思维,代码能力需要比较厉害
注意到数据非常大,所以数位dp的O(n)查询不可取
但 是 x 个 问 号 可 以 组 成 1 0 x 个 数 但是x个问号可以组成10^x个数 但是x个问号可以组成10x个数
所 以 高 位 的 ? 大 部 分 都 应 该 是 0 所以高位的?大部分都应该是0 所以高位的?大部分都应该是0
直 接 d p 后 30 个 问 号 即 可 , 这 样 前 面 问 号 都 填 0 , 有 1 0 30 个 数 字 直接dp后30个问号即可,这样前面问号都填0,有10^{30}个数字 直接dp后30个问号即可,这样前面问号都填0,有1030个数字
这 些 组 成 方 案 中 , 肯 定 超 过 了 k 个 数 满 足 条 件 这些组成方案中,肯定超过了k个数满足条件 这些组成方案中,肯定超过了k个数满足条件
至 于 d p , 也 很 巧 妙 , 2 ? 3 ? ? 拆 分 为 20300 和 0 ? 0 ? ? 至于dp,也很巧妙,2?3??拆分为20300和0?0?? 至于dp,也很巧妙,2?3??拆分为20300和0?0??
这 样 可 以 直 接 d p 问 号 了 这样可以直接dp问号了 这样可以直接dp问号了
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=50009;
const int mod=1e9+7;
int quick_pow( int a,int b,int mo )
{
int ans=1;
while( b )
{
if( b&1 ) ans=ans*a%mo;
a=a*a%mo;
b>>=1;
} return ans;
}
int dp[maxn][22],pos[maxn],pm[maxn],pmod[maxn];
string res;
int n,m,q;
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int T; cin >> T;
while( T-- )
{
cin >> n >> m >> q;
cin >> res;
int len=res.length(),cnt=0;
int amod=0,am=0;
for(int i=0;i<len;i++)
{
amod*=10,am*=10;
if( res[i]!='?' )
{
amod+=res[i]-'0';
am+=res[i]-'0';
}
amod%=mod,am%=m;
}
for(int i=len-1;i>=0;i--)
if( res[i]=='?' ) pos[++cnt]=len-i-1;
am=( m-am )%m;//还需要这么多
for(int i=1;i<=cnt;i++)
{
pm[i]=quick_pow(10,pos[i],m);
pmod[i]=quick_pow(10,pos[i],mod);
}
for(int i=0;i<=cnt;i++)
for(int j=0;j<20;j++)
dp[i][j]=0;
dp[0][0]=1;
for(int i=1;i<=cnt;i++)//后往前dp
for(int j=0;j<=9;j++)
{
int now=j*pm[i]%m;//尝试放数字j
for(int k=0;k<m;k++)
{
int ne=(now+k)%m;
dp[i][ne]+=dp[i-1][k];
dp[i][ne]=min( dp[i][ne],(int)1e18);
}
}
while( q-- )
{
int ans=amod,k;
cin >> k;
if( dp[cnt][am]<k )
{
cout << -1 << '\n';
continue;
}
int now=am;
for(int i=min(cnt,(int)30);i>=1;i-- )
for(int j=0;j<=9;j++)//当前位放j
{
int ne=( (now-j*pm[i]%m )+m )%m;//上一个状态
if( dp[i-1][ne]<k ) k-=dp[i-1][ne];
else
{
ans+=j*pmod[i]%mod;
ans%=mod;
now=ne;
break;
}
}
cout << ans % mod << '\n';
}
}
}