【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;
}