HDU6644 11 Dimensions(动态规划)

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[i1][jkpow(10,w[i])],k=09,这里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);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Best KeyBoard

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值