BZOJ1853 Scoi2010 幸運數字 【列舉+容斥】
阿新 • • 發佈:2018-12-10
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在這裡是沒有意義的,這樣篩一下大概就剩1000個左右,然後我們考慮暴力容斥,對於一個數w,在晒完的數裡面如果有p個約數,那麼我們把答案加上, 這個容斥我不證明,理解一下還是很簡單的
#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;
}