CF55D

题意:给定区间[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;
}

  

猜你喜欢

转载自www.cnblogs.com/oi-forever/p/9125839.html