• 【LG2567】[SCOI2010]幸运数字


    【LG2567】[SCOI2010]幸运数字

    题面

    洛谷

    题目大意:

    问你区间([L,R](1leq Lleq Rleq 10^{10}))中有几个数是仅由(6,8)组成的数的倍数。

    题解

    首先考虑容斥。

    但是这种数字去掉有倍数关系的数还有(943)个,还是无法直接容斥。

    这时候可以借鉴一下(meet;in;the;middle)的方式进行处理。

    发现去掉前(20)个数后,我们剩下的数的倍数加起来只有(10^6)级别了,那么我们对于前面(20)个数的情况,我们直接容斥解决,后面的数字我们可以全部枚举出来然后排序去重。

    然后这样的话还有一个小问题,就是前后合并时可能会算重,这样的话直接枚举后面每个数然后判断一下是否是前面的数的倍数即可。

    复杂度经过分析其实还是可以接受的,就是常数有点大,你可以考虑开个(O2)或者(20 ightarrow 19)再用哈希表去重。

    代码

    #include <iostream> 
    #include <cstdio> 
    #include <cstdlib> 
    #include <cstring> 
    #include <cmath> 
    #include <algorithm> 
    #include <vector> 
    using namespace std; 
    int tot; 
    unsigned long long L, R, t[3000000]; 
    bool vis[3000]; 
    vector<unsigned long long> num; 
    unsigned long long fac[1000]; 
    int siz; 
    void dfs(int x, unsigned long long res) { 
    	if (res <= R && res) num.push_back(res); 
    	if (x == tot + 1) return ; 
    	dfs(x + 1, res * 10 + 6); 
    	dfs(x + 1, res * 10 + 8); 
    } 
    long long solve() { 
    	long long ans = 0; 
    	for (register int s = 1, l = min(siz, 19); s < 1 << l; s++) { 
    		unsigned long long lcm = 0; int tt = 0; 
    		for (register int i = 0; i < l; i++) 
    			if (s >> i & 1) { 
    				if (lcm) lcm = lcm * fac[i] / __gcd(fac[i], lcm); 
    				else lcm = fac[i];
    				++tt; 
    			} 
    		ans += (tt & 1 ? 1 : -1) * (R / lcm - (L - 1) / lcm); 
    	} 
    	if (siz <= 19) return ans; 
    	tot = 0; 
    	for (int i = 19; i < siz; i++) 
    		for (unsigned long long j = R / fac[i] * fac[i]; j >= L; j -= fac[i]) 
    			t[++tot] = j; 
    	sort(&t[1], &t[tot + 1]); 
    	tot = unique(&t[1], &t[tot + 1]) - t - 1; 
    	for (int i = 1; i <= tot; i++)  { 
    		bool flag = 1; 
    		for (int j = 0; j < 19 && flag; j++) 
    			if (t[i] % fac[j] == 0) flag = 0; 
    		ans += flag; 
    	} 
    	return ans; 
    } 
    int main () { 
    #ifndef ONLINE_JUDGE 
        freopen("cpp.in", "r", stdin); 
    #endif 
    	cin >> L >> R; 
    	long long tmp = R; 
    	while (tmp) ++tot, tmp /= 10; 
    	dfs(1, 0); 
    	sort(num.begin(), num.end()); 
    	for (int i = 0; i < (int)num.size(); i++) { 
    		if (vis[i]) continue; 
    		fac[siz++] = num[i]; 
    		for (int j = i + 1; j < (int)num.size(); j++) 
    			if (num[j] % num[i] == 0) vis[j] = 1; 
    	} 
    	printf("%lld
    ", solve()); 
        return 0; 
    } 
    
  • 相关阅读:
    基础数据补充
    购物车
    小数据池、深浅拷贝和集合
    列表、元组和range
    小数据池、深浅拷贝和集合练习
    字典
    字符串练习
    列表练习
    练习
    字典练习
  • 原文地址:https://www.cnblogs.com/heyujun/p/11765165.html
Copyright © 2020-2023  润新知