[BZOJ2393] Cirno的完美算数教室(dfs+容斥原理)

传送门

 

先通过dfs预处理出来所有只有2和9的数,也就大概2000多个。

想在[L,R]中找到是这些数的倍数的数,可以通过容斥原理

那么如果a % b == 0,那么便可以把 a 去掉,因为 b 的倍数肯定包括 a 的倍数,那么就会只剩500多个数

然后我们dfs枚举所有数的可能,并顺便求出他们之间的lcm,选出来的数的个数,如果是奇数就对答案有正的贡献,如果是偶数就对答案有负的贡献

期间如果最小公倍数lcm>R的话就直接return,这个剪枝能省去大部分时间,以至AC

而在[L,R]区间的答案就是ans[1,R] - ans[1,L-1]

 

#include <cstdio>
#include <algorithm>
#define N 10001
#define LL long long

int n, cnt = -1;
LL l, r, ans, f[N], num[N];

inline LL gcd(LL x, LL y)
{
	return !y ? x : gcd(y, x % y);
}

inline void dfs(LL v)
{
	if(v > r) return;
	num[++cnt] = v;
	dfs(v * 10 + 2);
	dfs(v * 10 + 9);
}

inline void dfs_again(int i, int c, LL lcm)
{
	if(i == n + 1)
	{
		if(c & 1) ans += r / lcm - (l - 1) / lcm;
		else if(c) ans -= r / lcm - (l - 1) / lcm;
		return;
	}
	dfs_again(i + 1, c, lcm);
	LL tmp = lcm * f[i] / gcd(lcm, f[i]);
	if(tmp <= r) dfs_again(i + 1, c + 1, tmp);
}

int main()
{
	int i, j;
	scanf("%lld %lld", &l, &r);
	dfs(0);
	for(i = 1; i <= cnt; i++)
	{
		if(!num[i]) continue;
		f[++n] = num[i];
		for(j = i + 1; j <= cnt; j++)
			if(num[j] % num[i] == 0) num[j] = 0;
	}
	std::sort(f + 1, f + n + 1);
	dfs_again(1, 0, 1);
	printf("%lld\n", ans);
	return 0;
}

  

posted @ 2017-10-13 21:17  zht467  阅读(207)  评论(0编辑  收藏  举报