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
1 10
【样例输入2】
1234 4321
Sample Output
【样例输出1】
2
【样例输出2】
809
2
【样例输出2】
809
HINT
【数据范围】
对于30%的数据,保证1 < =a < =b < =1000000
对于100%的数据,保证1 < =a < =b < =10000000000
正解:搜索+容斥原理。
首先把所有幸运数搜索出来,最多只会有$2046$个。
然后我们考虑容斥原理,即暴力枚举每个数用不用,然后加减一下贡献即可。
但是极限数据跑不过,我们加两个剪枝。
首先我们把所有幸运数从大到小搜索,这样留给后面的状态就会变少。
然后我们把是一个幸运数数的倍数的幸运数去掉,可以发现这是不会影响答案的,并且可以减少状态。
然后注意求$lcm$可能会爆$long \ long$,我们使用$double$即可。这样我们就能通过此题了。
1 #include <bits/stdc++.h> 2 #define il inline 3 #define RG register 4 #define ll long long 5 6 using namespace std; 7 8 ll st[5010],ss[5050],vis[5050],tp,top,ans,a,b; 9 10 il ll cmp(const ll &a,const ll &b){ return a>b; } 11 12 il ll gcd(RG ll a,RG ll b){ return b ? gcd(b,a%b) : a; } 13 14 il double lcm(RG ll a,RG ll b){ return 1.0*a/gcd(a,b)*b; } 15 16 il void dfs0(RG ll x,RG ll tot){ 17 if (tot>b) return; if (tot) st[++top]=tot; 18 dfs0(x+1,tot*10+6),dfs0(x+1,tot*10+8); return; 19 } 20 21 il void dfs(RG ll x,RG ll tot,RG ll op){ 22 if (x>top){ 23 if (tot==1) return; 24 if (op) ans+=b/tot-(a-1)/tot; 25 else ans-=b/tot-(a-1)/tot; 26 return; 27 } 28 dfs(x+1,tot,op); RG double res=lcm(tot,st[x]); 29 if (res>b) return; dfs(x+1,res,op^1); return; 30 } 31 32 int main(){ 33 #ifndef ONLINE_JUDGE 34 freopen("number.in","r",stdin); 35 freopen("number.out","w",stdout); 36 #endif 37 cin>>a>>b,dfs0(1,0); 38 for (RG int i=1;i<top;++i) 39 for (RG int j=i+1;j<=top;++j) 40 if (st[i]%st[j]==0) vis[i]=1; 41 else if (st[j]%st[i]==0) vis[j]=1; 42 for (RG int i=1;i<=top;++i) if (!vis[i]) ss[++tp]=st[i]; 43 for (RG int i=1;i<=tp;++i) st[i]=ss[i]; top=tp; 44 sort(st+1,st+top+1,cmp),dfs(1,1,0),cout<<ans; return 0; 45 }