题目
Problem Description
11 Dimensions is a cute contestant being talented in math. One day, 11 Dimensions came across a problem but didn’t manage to solve it. Today you are taking training here, so 11 Dimensions turns to you for help.
You are given a decimal integer S with n bits s1s2…sn(0≤si≤9), but some bits can not be recognized now(replaced by ``?’’). The only thing you know is that S is a multiple of a given integer m.
There may be many possible values of the original S, please write a program to find the k-th smallest value among them. Note that you need to answer q queries efficiently.
Input
The first line of the input contains an integer T(1≤T≤10000), denoting the number of test cases.
In each test case, there are three integers n,m,q(1≤n≤50000,2≤m≤20,1≤q≤100000) in the first line, denoting the length of S, the parameter m, and the number of queries.
In the second line, there is a string s of length n, denoting the given decimal integer S. It is guaranteed that si is either an integer within [0,9] or ``?’’, and s1 is always an integer within [1,9].
For the next q lines, each line contains an integer ki(1≤ki≤1018), denoting each query.
It is guaranteed that ∑n≤500000 and ∑q≤106.
Output
For each query, print a single line containing an integer, denoting the value of S. If the answer exists, print Smod(109+7) instead, otherwise print ``-1’’.
Sample Input
1
5 5 5
2??3?
1
2
3
100
10000
Sample Output
20030
20035
20130
24935
-1
解析
其实这题比赛的时候就应该A出来了,之所以没A出来。。。还是因为嫩啊(没有特判没有问号的情况)。
首先,看到题目,基本能感觉到数位DP的气息。但是这里一个问题在于数的位数最多可以达到50000,加上
1
e
6
1e6
1e6的查询次数,妥妥的T,怎么办呢?其实我们根本没必要把50000位全部算一遍,首先筛选出问号所在地,然后只需要考虑后面若干个(我取了30)问号就可以了。为什么呢?因为
m
m
m是小于20的,也就是说,后面30个问号可以得到
1
0
30
10^{30}
1030个数,在这么多数里,仍然凑不够
1
0
18
10^{18}
1018个整除
m
m
m的数的概率是很低的,我们有理由相信这已经完全覆盖了,所以这里的剪枝就大大优化了复杂度(如果担心还是会超,可以用一个保险的方法,在DP的过程里,确认了某一位符合的数字个数已经超过了
1
0
18
10^{18}
1018,然后直接在本位剪枝,设置断点,结束打表过程,后面查询直接从剪枝位开始,前面的都不需要了)。
上代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 5e4 + 10;
#define ll long long
ll dp[40][10][25], tp[40][25];
const ll mod = 1e9 + 7;
const ll lmt = 1e18 + 10;
ll mm[maxn];
int n, m, q, cnt;
int num[maxn], d[maxn], t[maxn];
ll query(ll k, ll ans)
{
if(cnt == 0){
if(k==1 && tp[0][0]==1) return ans;
return -1;
}
ll res = 0;
int tmp = 0, a, b;
for(int i = cnt; i > 0; i--){
a = -1, b = (m-tmp)%m;
for(int j = 0; j < 10; j++){
if(k > dp[i][j][b]) k -= dp[i][j][b];
else {a = j;break;}
}
if(a == -1) return -1;
tmp += t[d[i]]*a, tmp %= m;
res += (ll)a * mm[d[i]], res %= mod;
}
return (res+ans)%mod;
}
void ddp(int tmp)
{
tp[0][tmp] = 1;
for(int i = 1; i <= cnt; i++){
for(int j = 0; j < 10; j++){
for(int p = 0; p < m; p++){
int a = (p - j*t[d[i]]%m + m)%m;
dp[i][j][p] += tp[i-1][a];
tp[i][p] += tp[i-1][a];
if(dp[i][j][p] > lmt) dp[i][j][p] = lmt;
if(tp[i][p] > lmt) tp[i][p] = lmt;
}
}
}
}
void solve()
{
scanf("%d%d%d\n", &n, &m ,&q);
t[0] = 1;
for(int i = 1; i < n; i++) t[i] = t[i-1]*10 % m;
ll res = 0, k;
memset(dp, 0, sizeof(dp));
memset(tp, 0, sizeof(tp));
char c; cnt = 0;
int tmp = 0;
for(int i = n-1; i >= 0; i--){
scanf("%c", &c);
if(c=='?') num[i] = -1, d[++cnt] = i;
else num[i]=c-'0', res += (ll)num[i]*mm[i], res %= mod,tmp = (tmp+t[i]*num[i])%m;
}
sort(d+1, d+1+cnt);
cnt = min(cnt, 30);
ddp(tmp);
for(int i = 0; i < q; i++){
scanf("%lld", &k);
printf("%lld\n", query(k, res));
}
}
int main()
{
mm[0] = 1;
for(int i = 1; i < maxn; i++)
mm[i] = mm[i-1] * 10 % mod;
int t;
scanf("%d", &t);
for(int i = 0; i < t; i++)
solve();
}