BZOJ 1799 同类分布(数位DP)
给出a,b,求出[a,b]中各位数字之和能整除原数的数的个数。1<=a<=b<=1e18.
注意到各位数字之和最大是153.考虑枚举这个东西。那么需要统计的是[0,a-1]和[0,b]内各位数字之和为x且能整除x的数字个数。
那么我们只需要数位dp一波即可。
令dp[pos][i][x]表示有pos位且数字之和为x的数mod P=i的数字个数。
则转移方程显然可得。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi acos(-1.0) # define eps 1e-9 # define MOD 1024523 # define INF 1000000000 # define mem(a,b) memset(a,b,sizeof(a)) # define FOR(i,a,n) for(int i=a; i<=n; ++i) # define FO(i,a,n) for(int i=a; i<n; ++i) # define bug puts("H"); # define lch p<<1,l,mid # define rch p<<1|1,mid+1,r # define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; int Scan() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } const int N=55; //Code begin... LL dp[20][165][165], p[20]; int wei[20]; LL dfs(int pos, int mod, int limit, int x, int P){ if (x<0) return 0; if (pos==0) return mod==0&&x==0; if (!limit&&~dp[pos][mod][x]) return dp[pos][mod][x]; int up=limit?wei[pos]:9; LL res=0; FOR(i,0,up) res+=dfs(pos-1,(mod+P-(i*p[pos-1]%P))%P,limit&&i==wei[pos],x-i,P); if (!limit) dp[pos][mod][x]=res; return res; } LL sol(LL x){ int pos=0; while (x) wei[++pos]=x%10, x/=10; LL res=0; int top=min(162,pos*9); FOR(i,1,top) mem(dp,-1), res+=dfs(pos,0,1,i,i); return res; } int main () { LL a, b; p[0]=1; FO(i,1,20) p[i]=p[i-1]*10; scanf("%lld%lld",&a,&b); printf("%lld\n",sol(b)-sol(a-1)); return 0; }