1. 程式人生 > >【bzoj1853】[Scoi2010]幸運數字 容斥原理+搜索

【bzoj1853】[Scoi2010]幸運數字 容斥原理+搜索

輸出 ble std pre urn 數據 統計 號碼 容斥

題目描述

在中國,很多人都把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]幸運數字 容斥原理+搜索