HDU6644 11 Dimensions(动态规划)
题意
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.
大概题意是这样的,有一个n位的数字,里面有一些位上的数字无法辨认了,但是我们知道它是m的倍数,有q次询问,每次询问可能的数字中第k小的数是多少,输出它对1e9+7取模之后的数
多组样例,样例数小于10000,n小于50000,q小于100000,每次询问的k小于1e18,m小于20。样例保证所有样例里的n的总和小于500000,q的总和小于1000000。
思路
这道题的数据都很大,唯一的好消息就是m比较小,所有显而易见的,这里就是题目的切入点了。由于m很小,所以可以想象到,只有最后的几十个问号可能会填上数,其他的问号上全是0。因为几十个问号足以构成很多种可能的数字,靠前的问号一点点的变动,哪怕只是加1,形成的数在所有可能的数里的排序也会远远超过k,所以我们只需要对于每次询问都枚举后几十个问号,这里为了能快速的求出每个问号填了一个数之后这个数字的序号会变大多少,需要用dp预处理一下。 d p [ i ] [ j ] dp[i][j] dp[i][j]代表只填后i个问号,填出来的数对m取模结果是j的方案数。所以, d p [ i ] [ j ] = ∑ d p [ i − 1 ] [ j − k ∗ p o w ( 10 , w [ i ] ) ] , k = 0 − 9 dp[i][j] = \sum dp[i-1][j-k*pow(10,w[i])],k = 0-9 dp[i][j]=∑dp[i−1][j−k∗pow(10,w[i])],k=0−9,这里w[i]就是第i个问号所在的位数。这个pow的值也是可以提前预处理好的。代码如下,我没有一个个得去枚举问号,而是每次二分找到需要填的最高位的问号,这样速度会快一点
#include <bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(ll i = (ll)j;i <= (ll)k;i ++)
#define debug(x) cerr<<#x<<":"<<x<<endl
#define pb push_back
typedef long long ll;
typedef pair<ll,ll> pi;
const ll mod =(ll)1e9+7;
ll n,m,dp[50009][22];
char s[50009];
ll w[50009];
ll vv[22][50009],v[50009];
ll cnt;
int main()
{
ll T;
scanf("%lld",&T);
v[0] = 1;
for(int i=1;i<=50000;i++)v[i] = (v[i-1]*10)%mod;
for(int i=2;i<=20;i++){
vv[i][0] = 1;
for(int j=1;j<=50000;j++)vv[i][j] = (vv[i][j-1]*10)%i;
}
while (T --) {
ll q;
scanf("%lld%lld%lld",&n,&m,&q);
scanf("%s",s);
ll a = 0,x = 0;
cnt=0;
for(int i=n-1;i>=0;i--){
if(s[i]!='?'){
a = (a + (s[i]-'0')*v[n-1-i])%mod;
x = (x+(s[i]-'0')*vv[m][n-1-i])%m;
}
else {
cnt++;
w[cnt] = n-1-i;
}
}
for(int i=0;i<m;i++)dp[0][i]=0;
dp[0][x]=1;
for(int i=1;i<=cnt;i++){
for(int j=0;j<m;j++){
dp[i][j]=0;
for(int k=0;k<=9;k++)dp[i][j]+=dp[i-1][(j-k*vv[m][w[i]]+k*m)%m];
}
if(dp[i][0]>=(ll)(1e18)){
cnt=i;
}
}
while(q--){
ll k;
scanf("%lld",&k);
ll ans = a;
ll t = 0;
if(k>dp[cnt][t]){
printf("-1\n");
continue;
}
while(k){
ll l = 0,r = cnt;
while(l<=r){
ll mid = (l+r)/2;
if(dp[mid][t]<k)l = mid+1;
else r=mid-1;
}
if(l==0)break;
else {
for(int i = 0;i<=9;i++){
if(dp[l-1][(t-i*vv[m][w[l]]+i*m)%m]<k)
k-=dp[l-1][(t-i*vv[m][w[l]]+i*m)%m];
else {
ans=(ans+i*v[w[l]])%mod;
t=(t-i*vv[m][w[l]]+i*m)%m;
break;
}
}
}
}
printf("%lld\n",ans);
}
}
}