【Luogu】P5261 [JSOI2013]数字理论 题解

数位 DP 好题。

为啥没人用 bitset 啊...

不妨从低位往高位 DP。设 \(f(i,s,x,p)\) 表示当前 DP 到第 \(i\) 位,原数数位和为 \(s\)\(\times d\) 后给第 \(i+1\) 位的进位为 \(x\)\(\times d\) 后的数位和为 \(p\)(不包括 \(x\))是否可行。

枚举第 \(i+1\) 位的取值 \(j\),有转移:

\[f(i+1,s+j,\left\lfloor \frac{x+jd}{10} \right\rfloor,p+((x+jd) \bmod 10)) \gets \\ f(i+1,s+j,\left\lfloor \frac{x+jd}{10} \right\rfloor,p+((x+jd) \bmod 10)) \operatorname{or} f(i,s,x,p) \]

其中 \([i=k-1]<j\le 9\),因为最高位不能是 \(0\)。不难发现这可以用 bitset 优化。

输出方案也是可行的。首先枚举最高位(第 \(k\) 位)的进位 \(x\),再从高往低枚举位 \(i\),最后枚举当前位的取值 \(j\) 和第 \(i-1\) 位给第 \(i\) 位的进位 \(x'\),则该取值可行当且仅当:

  • \(f(i-1,s-j,x',(p-jd+px) \bmod 10)=1\)
  • \(\left\lfloor \dfrac{px+jd}{10} \right\rfloor = x\)

之后执行操作:

  • \(x \gets x'\)
  • \(s \gets s-j\)
  • \(p \gets (p-jd+px) \bmod 10\)
  • \(ans_i \gets j\)
  • \(i \gets i-1\)

直至 \(i=0\)

最后答案就是 \(ans\)

时间复杂度 \(O(ks\frac{p}{\omega} \times 10^2)\),其中 \(\omega=64\)

#include <bits/stdc++.h>
using namespace std;

typedef long long i64;
typedef vector<int> vi;
typedef pair<int,int> pii;

template<typename T>
inline T read(){
    T x=0,f=0;char ch=getchar();
    while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
    while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
    return f?-x:x;
}

#define rdi read<int>
#define rdi64 read<i64>
#define fi first
#define se second
#define pb push_back
#define mp make_pair

const int K=110,S=1023;

int k,s,p,d;
bitset<S> f[K][S][10];
int res[K];

int main(){
    k=rdi(),s=rdi(),p=rdi(),d=rdi();
    f[0][0][0][0]=1;
    for(int i=0;i<k;i++){
        for(int j=0;j<=i*9;j++){
            for(int x=0;x<10;x++){
                for(int cur=i==k-1;cur<=9;cur++){
                    int sum=x+cur*d;
                    f[i+1][j+cur][sum/10]|=(f[i][j][x]<<(sum%10));
                }
            }
        }
    }
    bool fl=0;
    for(int x=0;x<10;x++){
        if(p>=x&&f[k][s][x][p-x]){
            p-=x,fl=1;
            for(int i=k;i>=1;i--){
                for(int cur=i==k;cur<=9;cur++){
                    for(int px=0;px<10;px++){
                        int ps=s-cur,sum=cur*d+px;
                        if(ps<0||sum/10!=x) continue;
                        int pp=p-sum%10;
                        if(pp<0||!f[i-1][ps][px][pp]) continue;
                        res[i]=cur,x=px,s=ps,p=pp;
                        goto nxt;
                    }
                }
                nxt:;
            }
            break;
        }
    }
    if(!fl) puts("-1");
    else for(int i=k;i>=1;i--) putchar(res[i]+'0');
    return 0;
}

posted @ 2022-06-23 20:29  Zesty_Fox  阅读(113)  评论(4编辑  收藏  举报