[BZOJ1853][SCOI2010]幸运数字(DFS+容斥)

来源:互联网 发布:let it go 编辑:程序博客网 时间:2024/05/21 18:37

先预处理[1,1010]内的所有幸运号码。
对于一个幸运号码X,在[A,B]X的倍数的个数,就是
BXAX+1
然而,两个幸运号码对应的近似幸运号码可能有交集。考虑到这一点,就可以用容斥,也就是:
1个幸运号码2个幸运号码的lcm+3个幸运号码的lcm...
但是直接做是22046的,啃腚会TLE。考虑3个剪枝:
1、发现对于两个幸运号码a,b,如果a|b,那么对于所有的b|x就一定有a|x,因此这样的b是不必要的。去掉所有这样的b后,还剩下943个幸运号码。
2、当前的lcm一旦大于B就不再继续搜索。这样复杂度就能大大降低。
3、将预处理出的幸运号码从大到小排序,使lcm能更快地超越上界B
代码:

#include <cmath>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;typedef long long ll;const int N = 1e4 + 5;ll A, B, a[N], num[N], Ans;int tot, n; bool mark[N];void dfs1(int dep, int cnt, ll now, ll x) {    if (dep > cnt) return (void) (a[++tot] = now);    dfs1(dep + 1, cnt, now + x * 6, x * 10);    dfs1(dep + 1, cnt, now + x * 8, x * 10);}void sieve() {    int i, j;    for (i = 1; i <= tot; i++) {        if (!mark[i]) num[++n] = a[i];        for (j = i + 1; j <= tot; j++)            if (a[j] % a[i] == 0) mark[j] = 1;    }}ll Cnt(ll l, ll r, ll PYZ) {    l = l / PYZ + (l % PYZ != 0); r /= PYZ;    return r - l + 1;}void dfs2(int dep, int cnt, ll val) {    if (val > B) return;    if (dep > n) {        if (cnt == 0) return;        Ans += Cnt(A, B, val) * ((cnt & 1) ? 1 : -1);        return;    }    dfs2(dep + 1, cnt, val);    ll tmp = val / __gcd(val, num[dep]);    if (1.0 * tmp * num[dep] <= B)        dfs2(dep + 1, cnt + 1, tmp * num[dep]);}bool comp(ll a, ll b) {return a > b;}int main() {    int i; cin >> A >> B;    for (i = 1; i <= 10; i++) dfs1(1, i, 0, 1);    sieve(); sort(num + 1, num + n + 1, comp);    cout << (dfs2(1, 0, 1), Ans) << endl;    return 0;}
原创粉丝点击