[SCOI2010]幸運數字
阿新 • • 發佈:2018-02-21
省選 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]幸運數字