【bzoj1853】[Scoi2010]幸運數字 容斥原理+搜索
題目描述
在中國,很多人都把6和8視為是幸運數字!lxhgww也這樣認為,於是他定義自己的“幸運號碼”是十進制表示中只包含數字6和8的那些號碼,比如68,666,888都是“幸運號碼”!但是這種“幸運號碼”總是太少了,比如在[1,100]的區間內就只有6個(6,8,66,68,86,88),於是他又定義了一種“近似幸運號碼”。lxhgww規定,凡是“幸運號碼”的倍數都是“近似幸運號碼”,當然,任何的“幸運號碼”也都是“近似幸運號碼”,比如12,16,666都是“近似幸運號碼”。 現在lxhgww想知道在一段閉區間[a, b]內,“近似幸運號碼”的個數。
輸入
輸入數據是一行,包括2個數字a和b
輸出
輸出數據是一行,包括1個數字,表示在閉區間[a, b]內“近似幸運號碼”的個數
樣例輸入
【樣例輸入1】
1 10
【樣例輸入2】
1234 4321
樣例輸出
【樣例輸出1】
2
【樣例輸出2】
809
題解
容斥原理+搜索
首先“幸運號碼”最多只有$2^1+2^2+...+2^{10}$個,因此可以把它們預處理出來。
然後要統計的就是這些數中是某數倍數的數,可以考慮容斥,無限制的-必須是1個的倍數的+必須是2個的倍數的-...得到不符合條件的。
而$[1,n]$中$x$的倍數有$\lfloor\frac nx\rfloor$個,因此可以直接$O(1)$得出。
那麽直接搜索+Lcm即可。
但是這樣做會TLE,需要優化。
考慮:如果$x$是$y$的倍數,那麽$x$對答案的貢獻一定為0。因此可以先篩掉所有倍數,然後再dfs即可。
時間復雜度$O(玄學)=O(能過)$
由於求Lcm可能會爆long long,因此使用了long double。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; ll w[5000] , a , b , ans; int n = -1; bool cmp(ll a , ll b) {return a > b;} void init(ll x) { if(x > b) return; w[++n] = x; init(x * 10 + 6) , init(x * 10 + 8); } ll gcd(ll a , ll b) { return b ? gcd(b , a % b) : a; } void calc(int p , long double now , int flag) { if(now > b) return; ll t = now; long double tmp; ans += (b / t - a / t) * flag; int i; for(i = p + 1 ; i <= n ; i ++ ) if((tmp = now * w[i] / gcd(t , w[i])) <= b) calc(i , tmp , -flag); } int main() { int i , j; scanf("%lld%lld" , &a , &b) , a -- ; init(0); for(i = 1 ; i <= n ; i ++ ) for(j = 1 ; j <= n ; j ++ ) if(i != j && w[j] && w[i] % w[j] == 0) w[i] = 0; sort(w + 1 , w + n + 1 , cmp); for(n = 0 ; w[n + 1] ; n ++ ); calc(0 , 1 , 1); printf("%lld\n" , b - a - ans); return 0; }
【bzoj1853】[Scoi2010]幸運數字 容斥原理+搜索