题意:给定区间[L,R],求区间完美数字的个数。(一个数字是完美数字当且仅当该数字可整除其所有数位上的非零数)
总结:位数上的数字只需要考虑2~9,因此用一个数字1<<8
来表示有哪些数字出现过。
如果一个数是所有位数的倍数,那么一定是其最小公倍数的倍数,其所有的最小公倍数是2520,所以求和的时候直接对2520取余。dp[l][cnt][sum]
,l为数字长度,cnt为位数上有哪些数,sum为数字和。dfs(int l,int cnt,int sum,bool sig)
,sig为是否为边界。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int MOD = 2520; #define int ll ll dp[20][1 << 8][2520]; int t, dight[20], tot; ll dfs(int l, int cnt, int sum, bool sig) { if(l == 0) { for (int i = 2; i <= 9; ++i) { if((cnt & (1 << (i - 2))) && (sum % i != 0)) return 0; } return 1; } if(!sig && dp[l][cnt][sum] != -1) return dp[l][cnt][sum]; int nex = (sig ?dight[l] :9); ll res = 0; for (int i = 0; i <= nex; ++i) { res += (i < 2) ?dfs(l - 1, cnt, (sum * 10 + i) % MOD, sig && (i == nex)) :dfs(l - 1, cnt | (1 << (i - 2)), (sum * 10 % MOD + i) % MOD, sig && (i == nex)); } if(!sig) dp[l][cnt][sum] = res; return res; } ll calc(int a) { tot = 0; while(a) { dight[++tot] = a % 10; a /= 10; } return dfs(tot, 0, 0, true); } main() { scanf("%lld", &t); memset(dp, -1, sizeof dp); while(t--) { ll l, r; scanf("%lld%lld", &l, &r); printf("%lld\n", calc(r) - calc(l - 1)); } return 0; }