题意:求出l到r中,能整除自己各非0位的和的数有多少个。
转换一下,就相当于求整除自己各非0位的最小公倍数,然后1到9公倍数范围是1到2520,会爆内存,考虑离散化。
转移过程有两种,一种是非0的,一种是数位是0的,是0的话最小公倍数不变,否则最小公倍数跟新的数求一下最小公倍数。
然后mod要一直取余2520,防止数组越界。
//多少个数能够整除自身各位数 //相当于 多少个数能够整除自身各位数的最小公倍数 #include <iostream> #include <algorithm> #include <string.h> #include <set> using namespace std; __int64 f[20][2521][50]; __int64 num[] = { 1,2,3,4,5,6,7,8,9,10, 12,14,15,18,20,21,24,28,30,35, 36,40,42,45,56,60,63,70,72,84, 90,105,120,126,140,168,180,210,252,280, 315,360,420,504,630,840,1260,2520 }; int a[20]; __int64 gcd(__int64 a, __int64 b) { return b == 0 ? a : gcd(b, a % b); } __int64 lc(__int64 a, __int64 b) { return a * b / gcd(a, b); } __int64 dp(__int64 pos, __int64 mod, __int64 lcm, bool flag) { if (pos == 0) return mod % lcm == 0; __int64 tmp = lower_bound(num, num + 48, lcm) - num; if (flag && f[pos][mod][tmp] != -1) return f[pos][mod][tmp]; int x = flag ? 9 : a[pos]; __int64 ans = 0; for (int i = 0; i <= x; i++) { if (i == 0) ans += dp(pos - 1, (mod * 10 + i) % 2520, lcm, flag || i < x); else ans += dp(pos - 1, (mod * 10 + i) % 2520, lc(lcm, i), flag || i < x); } if (flag) f[pos][mod][tmp] = ans; return ans; } __int64 cul(__int64 x) { int pos = 0; while (x) { a[++pos] = x % 10; x /= 10; } return dp(pos, 0, 1, false); } int main() { ios::sync_with_stdio(false); int t; cin >> t; memset(f, -1, sizeof(f)); while (t--) { __int64 l, r; cin >> l >> r; cout << cul(r) - cul(l - 1) << endl; } }
搞出num数组的方法,用下面这个程序输出的结果往上面拷贝
#include <iostream> #include <algorithm> #include <string.h> #include <set> using namespace std; set <int> st; int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); } int lc(int a, int b) { return a * b / gcd(a, b); } int main() { for (int i = 0; i < (1 << 9); i++) { int ans = 1; int ii = i; for (int j = 1; j <= 9; j++) { if (ii & 1) ans = lc(ans, j); ii >>= 1; } st.insert(ans); } int i = 0; for (set<int>::iterator it = st.begin(); it != st.end(); it++) { cout << *it << ","; i++; if (i % 10 == 0) cout << endl; } }