1. 程式人生 > >BZOJ1853 Scoi2010 幸運數字 【列舉+容斥】

BZOJ1853 Scoi2010 幸運數字 【列舉+容斥】

BZOJ1853 Scoi2010 幸運數字

Description

在中國,很多人都把6和8視為是幸運數字!lxhgww也這樣認為,於是他定義自己的“幸運號碼”是十進位制表示中只包含數字6和8的那些號碼,比如68,666,888都是“幸運號碼”!但是這種“幸運號碼”總是太少了,比如在[1,100]的區間內就只有6個(6,8,66,68,86,88),於是他又定義了一種“近似幸運號碼”。lxhgww規定,凡是“幸運號碼”的倍數都是“近似幸運號碼”,當然,任何的“幸運號碼”也都是“近似幸運號碼”,比如12,16,666都是“近似幸運號碼”。 現在lxhgww想知道在一段閉區間[a, b]內,“近似幸運號碼”的個數。

Input

輸入資料是一行,包括2個數字a和b

Output

輸出資料是一行,包括1個數字,表示在閉區間[a, b]內“近似幸運號碼”的個數

Sample Input

【樣例輸入1】 1 10 【樣例輸入2】 1234 4321

Sample Output

【樣例輸出1】 2 【樣例輸出2】 809

HINT

【資料範圍】 對於30%的資料,保證1 < =a < =b < =1000000 對於100%的資料,保證1 < =a < =b < =10000000000

首先我們可以把所有由6和8組成的數dfs出來,數量不多一共只有兩千個

然後我們發現對於

i%j==0,i在這裡是沒有意義的,這樣篩一下大概就剩1000個左右,然後我們考慮暴力容斥,對於一個數w,在晒完的數裡面如果有p個約數,那麼我們把答案加上,1P(r/w(l1)/w) 這個容斥我不證明,理解一下還是很簡單的

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define N 10010
vector<LL> number,v;
bool vis[N];
LL l,r,ans=0;
void dfs(LL tmp){
  if(tmp)number.push_back(tmp);
  if
(tmp*10+6<=r)dfs(tmp*10+6); if(tmp*10+8<=r)dfs(tmp*10+8); } LL gcd(LL a,LL b){return b?gcd(b,a%b):a;} void Find(int tmp,LL lcm,int sign){ if(tmp==(signed)v.size()){ if(lcm>1)ans+=(r/lcm-(l-1)/lcm)*sign; return; } Find(tmp+1,lcm,sign); double nxt=(double)v[tmp]/(double)gcd(v[tmp],lcm)*(double)lcm; if(nxt>r)return; Find(tmp+1,nxt,-sign); } void solve(){ dfs(0); sort(number.begin(),number.end()); int n=number.size()-1; for(int i=0;i<=n;i++)if(!vis[i]) for(int j=i+1;j<=n;j++) if(number[j]%number[i]==0)vis[j]=1; for(int i=0;i<=n;i++)if(!vis[i])v.push_back(number[i]); reverse(v.begin(),v.end()); } int main(){ scanf("%lld%lld",&l,&r); solve(); Find(0,1,-1); printf("%lld",ans); return 0; }