[Ahoi2009]self 同类分布
<body>
<center><h1>1799: [Ahoi2009]self 同类分布</h1><span class="green">Time Limit: </span>50 Sec <span class="green">Memory Limit: </span>64 MB<br><span class="green">Submit: </span>2357 <span class="green">Solved: </span>1079<br>[<a href="submitpage.php?id=1799">Submit</a>][<a href="problemstatus.php?id=1799">Status</a>][<a href="bbs.php?id=1799">Discuss</a>]</center><h2>Description</h2><div class="content">给出a,b,求出[a,b]中各位数字之和能整除原数的数的个数。
[Submit][Status][Discuss]
</body>
Sample Input
10 19
Sample Output
3
HINT
【约束条件】1 ≤ a ≤ b ≤ 10^18
Source
qingdaobaibai的题解
因为考虑到直接dp不可行,我们先枚举数字之和,共有9*18种,f[i][j][k][2]表示长度为i数字之和为j,模当前枚举的数字之和为k的是否严格小于该数的种类数。
那么f[i][j][k]-->f[i+1][j+p][(k*10+p)%mod]大概就是这样
#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
rg T data=0,w=1;rg char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') w=-1;ch=getchar();}
while(isdigit(ch)) data=data*10+ch-'0',ch=getchar();
return data*w;
}
template<class T>il T read(rg T&x) {return x=read<T>();}
typedef long long ll;
co int N=200,L=21;
ll f[L][N][N][2];
int n[L];
ll calc(ll x,int P){
if(!x) return 0;
memset(f,0,sizeof f);
int t=0;
while(x) n[++t]=x%10,x/=10;
f[t+1][0][0][0]=1;
for(int i=t+1;i>1;--i)
for(int j=0;j<=P;++j)
for(int k=0;k<P;++k)if(f[i][j][k][0]||f[i][j][k][1])
for(int p=0;p<10;++p){
int w=(10*k+p)%P;
if(p<n[i-1]&&j+p<=P) f[i-1][j+p][w][1]+=f[i][j][k][0];
else if(p==n[i-1]&&j+p<=P) f[i-1][j+p][w][0]+=f[i][j][k][0];
if(f[i][j][k][1]&&j+p<=P) f[i-1][j+p][w][1]+=f[i][j][k][1];
}
return f[1][P][0][0]+f[1][P][0][1];
}
int main(){
ll a=read<ll>(),b=read<ll>(),ans=0;
for(int i=1;i<=9*18;++i) ans+=calc(b,i)-calc(a-1,i);
printf("%lld\n",ans);
return 0;
}
静渊以有谋,疏通而知事。