您的位置:首页 > 其它

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: