[COCI2007-2008#3] CUDAK 解题记录
题意简述
给定闭区间 [ a , b ] [a,b] [a,b],以及一个整数 s s s,求区间 [ a , b ] [a,b] [a,b] 中有多少个数的各数位之和为 s s s 和最小的满足条件的数。
题目分析
这道题看起来和[入门赛 #21] 星云 hard ver.有点像,所以我们考虑使用数位 DP。
设
d
p
s
t
e
p
,
s
u
m
dp_{step,sum}
dpstep,sum 表示当前枚举到第
s
t
e
p
step
step 位,数位之和为
s
u
m
sum
sum 的满足条件的数的个数,因为
s
t
e
p
≤
15
,
s
u
m
≤
135
step \leq 15,sum \leq 135
step≤15,sum≤135,所以考虑记忆化搜索。
第一个问很水,就是模板,接下来考虑第二个问。(其实也很水)
直接枚举肯定不可能。因为求的数一定在
[
a
,
b
[a,b
[a,b] 之间,而这个区间内的数满足单调性,所以可以二分查找,找到最小值。
AC Code
#include<bits/stdc++.h>
#define arrout(a,n) rep(i,1,n)std::cout<<a[i]<<" "
#define arrin(a,n) rep(i,1,n)std::cin>>a[i]
#define rep(i,x,n) for(int i=x;i<=n;i++)
#define dep(i,x,n) for(int i=x;i>=n;i--)
#define erg(i,x) for(int i=head[x];i;i=e[i].nex)
#define dbg(x) std::cout<<#x<<":"<<x<<" "
#define mem(a,x) memset(a,x,sizeof a)
#define all(x) x.begin(),x.end()
#define arrall(a,n) a+1,a+1+n
#define PII std::pair<int,int>
#define m_p std::make_pair
#define u_b upper_bound
#define l_b lower_bound
#define p_b push_back
#define CD const double
#define CI const int
#define int long long
#define il inline
#define ss second
#define ff first
#define itn int
CI N=20;
int a,b,s,num[N],dp[N][150];
int dfs(int step,int sum,int st,int limit) {
if(step>num[0]) {
if(sum==s) {
return 1;
} else {
return 0;
}
}
if(!st&&!limit&&dp[step][sum]!=-1) {
return dp[step][sum];
}
int up=limit?num[num[0]-step+1]:9;
int ans=0;
rep(i,0,up) {
if(sum+i>s) {
break;
}
ans+=dfs(step+1,sum+i,st&&i==0,limit&&i==up);
}
if(!st&&!limit) {
dp[step][sum]=ans;
}
return ans;
}
int solve(int x) {
num[0]=0;
while(x) {
num[++num[0]]=x%10;
x/=10;
}
mem(dp,-1);
return dfs(1,0,1,1);
}
signed main() {
std::cin>>a>>b>>s;
std::cout<<solve(b)-solve(a-1)<<"\n";
int l=a,r=b,ans=0;
while(l<=r) {
int mid=(l+r)>>1;
if(solve(mid)-solve(a-1)>0) {
r=mid-1;
ans=mid;
} else {
l=mid+1;
}
}
std::cout<<ans;
return 0;
}