bzoj1853【scoi2010】幸运数字(暴力出奇迹)
2017-03-14 21:34
411 查看
时间限制:2秒 内存限制:128M
【问题描述】
在中国,很多人都把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
【数据范围】
对于30%的数据,保证1<=a<=b<=1000000
对于100%的数据,保证1<=a<=b<=10000000000
【来源】
bzoj1853
这道题我一看暴力嘛,直接做。
把所以的由6和8组成的数生成了出来,靠倍数去了一下(有它的因数的数不要,原因请自行理解),然后发现不行,要用容斥原理,数了一下gg。
当时不知道怎么想的真的就暴力出来了,前9个数用容斥,从第10个数开始就至少6666了,那就好办了。
我们先枚局每个数,再用它与所有比它小的数取次最大公倍数来除以比它小的那个数,这样我们可以得到他们最小公倍数是取出来的这个数的多少倍,然后我们直接标记这些倍数就可以了。
因为最小的数都有6666,所有倍数最多也就10000000000/6666了,自己算一下其实也就1500150.0150015,取个整最多1500151,完全可以标记的。
就这样发疯了的我就把它暴力出来了,俗话说的好,暴力出奇迹。
代码如下:
#include<cstdlib> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<vector> using namespace std; typedef long long ll; ll a,b; ll q[10000],p[2000][2000]; bool vis[10000],vis3[2000155]; int root=0,tot; ll tt; ll gcd(ll x,ll y) { return y==0?x:gcd(y,x%y); } void run(ll x) { if(x>b) return; q[++root]=x; run(x*10+6); run(x*10+8); } ll work2(ll n,int i)//看1~n这n个倍数中能出现的有几个,j是有几个数的倍数要去掉 { memset(vis3,0,sizeof(vis3)); sort(p[i]+1,p[i]+i); ll ans=0,tt2=0; for(int k=1;k<i&&p[i][k]<=n;k++) if(!vis3[p[i][k]]) for(int j=1;j*p[i][k]<=n;j++) if(!vis3[j*p[i][k]]) vis3[j*p[i][k]]=1,tt2++; return n-tt2; } ll check(ll x) { ll t=1; for(int i=1;i<10;i++) if(vis[i]) { t=q[i]/gcd(t,q[i])*t; if(t>x) break; } return x/t; } void run(int i,int j,ll x) { if(i==10||i==tot+1) { if(j==0) return; if(j%2) tt+=check(x); else tt-=check(x); return; } vis[i]=1; run(i+1,j+1,x); vis[i]=0; run(i+1,j,x); } ll work3(ll x) { memset(vis,0,sizeof(vis)); tt=0; run(1,0,x); return tt; } ll work(ll x)//算1~x中有多少伪幸运数 { ll ans=0,k; ans+=work3(x);//算前9个数的容斥 for(int i=10;i<=tot;i++) if(q[i]<=x)//后面的数用标记大法,标记它的哪些倍数可以出现 { for(int j=1;j<i;j++) p[i][j]=q[j]/gcd(q[i],q[j]); ans+=work2(x/q[i],i); } return ans; } int main() { //freopen("Luckynumber.in","r",stdin); //freopen("Luckynumber.out","w",stdout); cin>>a>>b; root=0; run(6);//生成幸运数 run(8); sort(q+1,q+1+root); memset(vis,1,sizeof(vis)); for(int i=root;i>0;i--) if(vis[i]) for(int j=1;j<i;j++)if(q[i]%q[j]==0) { vis[i]=0; break; } for(int i=1;i<=root;i++)if(vis[i])//去有因数的数 q[++tot]=q[i]; cout<<work(b)-work(a-1); return 0; }
相关文章推荐
- 【bzoj1853】[Scoi2010]幸运数字
- BZOJ1853 [Scoi2010]幸运数字 容斥原理
- bzoj1853[Scoi2010]幸运数字 容斥原理
- [bzoj1853][Scoi2010][幸运数字] (容斥原理)
- 【bzoj1853】【Scoi2010】【幸运数字】【搜索】
- [BZOJ1853][Scoi2010]幸运数字(容斥原理)
- BZOJ1853: [Scoi2010]幸运数字
- bzoj1853: [Scoi2010]幸运数字
- bzoj1853[Scoi2010]幸运数字 容斥
- BZOJ1853: [Scoi2010]幸运数字
- bzoj1853: [Scoi2010]幸运数字 dp+容斥原理
- bzoj1853: [Scoi2010]幸运数字(容斥原理)
- BZOJ1853:[SCOI2010]幸运数字 & BZOJ2393:Cirno的完美算数教室——题解
- bzoj1853【SCOI2010】幸运数字
- [BZOJ1853][SCOI2010]幸运数字(DFS+容斥)
- bzoj1853 [Scoi2010]幸运数字
- BZOJ 1853: [Scoi2010]幸运数字
- BZOJ 1853 【SCOI2010】 幸运数字
- 【SCOI2010】【BZOJ1853】幸运数字
- 【BZOJ1853/2393】[Scoi2010]幸运数字/Cirno的完美算数教室 DFS+容斥