1. 程式人生 > >[SCOI2010]幸運數字

[SCOI2010]幸運數字

省選 fonts orange ora 幸運數 容斥原理 說明 cst define

題目背景

四川NOI省選2010

題目描述

在中國,很多人都把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
輸出樣例#1:
2

說明

對於30%的數據,保證1<=a<=b<=1000000

對於100%的數據,保證1<=a<=b<=10000000000

解題思路:

(暴力都沒寫對,還是細心的問題。)

觀察可知,可以預處理出所有的幸運數字,不超過5000個

然後枚舉對應範圍內的合法數字,但是發現會有重復。怎們辦呢?

對於某個幸運數字,如果他是另一個幸運數字的倍數,就刪去他(因為枚舉另一個幸運數字的倍數時一定會枚舉到他)。

對於剩下的大約n==2000個幸運數字,搜索得到答案。

從n個數字中選取y個數字,最小公倍數為z,那麽貢獻為r/z-(l-1)/z;

但是依然會有重復,根據容斥原理(仔細想想):

就--> +1個的-2個的+3個的-4個的.......

(詳見代碼)

#include<iostream>
#include<cstdio>
#include<map>
#include<algorithm>
#define ll long long
using namespace std;
inline ll read()
{
    ll x=0,w=1;char ch=getchar();
    
while(!isdigit(ch)){if(ch==-) w=-1;ch=getchar();} while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-0,ch=getchar(); return x*w; } ll x,y,a[5000],k,ans,s[5000],p; bool fg[5000]; bool cmp(ll x,ll y){return x>y;} void dfs(int x,ll nw) { if(x==12) return; a[++k]=nw*10+6; dfs(x+1,a[k]); a[++k]=nw*10+8; dfs(x+1,a[k]); } ll gcd(ll x,ll y) { ll r=x%y; while(r) x=y,y=r,r=x%y; return y; } void DFS(int nw,int u,ll z) { if(nw>p) { if(u&1) ans+=y/z-(x-1)/z; else if(u) ans-=y/z-(x-1)/z; return; } DFS(nw+1,u,z);; ll tmp=z/gcd(s[nw],z); if((double)tmp*s[nw]<=y) DFS(nw+1,u+1,tmp*s[nw]); } int main() { dfs(1,0); for(int i=1;i<=k;++i) for(int j=1;j<=k;++j) if(j!=i && a[i]%a[j]==0) {fg[i]=1;break;} for(int i=1;i<=k;++i) if(!fg[i]) s[++p]=a[i]; sort(s+1,s+p+1,cmp); x=read();y=read(); DFS(1,0,1); printf("%lld",ans); return 0; }

交友須帶三分俠氣,作人要存一點素心 。

[SCOI2010]幸運數字