题意:
仅由4和7组成的十进制数字被称为幸运数字,如,4,7,44,47。
那么仅由幸运数字乘积得到的数字为超级幸运数字,如,28 = 4 * 7。
现在给你两个数字表示上下限,让你求这段区间内有多少个超级幸运数字。总共有1000组查询,上下限最多到10121012。
思路:
第一想法是容斥搞一下,但是其实非常难,因为除了爆搜很难去构造出仅由幸运数字乘起来得到的数。
第二想法是爆搜,既然是面试题,肯定不会难到哪里去。那就估算一下复杂度和爆搜的体量。
幸运数字的量:其实可以把4和7当做0和1,用二进制估算,大约是213213个,也就是不超过81928192。因此这部分可以轻而易举地预处理一下。
超级幸运数字的量:
首先,我们可以发现,4的幂全都是超级幸运数字,那么在10121012里,大致有19个4的幂。那么也就是说,通过其他途径进行组合的,每一列不会超过20个。
可以这么想,你拿着这个去做类似一个2819228192个的组合,然后就会发现,这样的搜索树,经过剪枝之后,几乎变成了一条链,而且被剪枝的搜索树是以幂次的下降的。所以绝对不会超过220220个超级幸运数。这个推理是没有根据的,但是8192log2(8192)8192log2(8192)这个量级的,所以总体也不是特别大。
所以就可以直接爆搜了,贴一个预处理完的代码。
#include
#include
#include
using namespace std;
long long p[10000];
int num = 0;
void init() {
for (int n = 1; n <= 12; n ++) {
for(int s = 0; s < (1 << n); s ++) {
long long t = 0;
for (int k = 0; k < n; k ++) {
if (s>>(n-k-1)&1) {
t = t * 10 + 7;
} else {
t = t * 10 + 4;
}
}
p[num ++] = t;
}
}
}
std::vector<long long> q;
long long solve(int pos, long long now, long long n) {
if (pos >= num) {
return 0;
}
int ans = 0;
if (p[pos] <= n / now) {
ans ++;
//q.push_back(now * p[pos]);
q.emplace_back(now * p[pos]);
ans += solve(pos, now * p[pos], n);
ans += solve(pos + 1, now, n);
}
return ans;
}
void print() {
for (int i = 0; i < num; i ++) {
printf("%lld%c", p[i], " \n"[i == num - 1]);
}
}
int main() {
init();
// print();
long long n = 1000000000000L;
long long ans = solve(0, 1, n);
printf("%d %d %lld\n", num, (int)q.size(), ans);
sort(q.begin(), q.end());
q.erase(unique(q.begin(), q.end()), q.end());
printf("%d %d %lld\n", num, (int)q.size(), ans);
}
[Scoi2010]幸运数字 dp+容斥原理
在中国,很多人都把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和bOutput输出数据是一行,包括1个数字,表示在闭区间[a, b]内“近似幸运号码”的个数Sample Input
【样例输入1】
1 10
【样例输入2】
1234 4321
Sample Output
【样例输出1】
2
【样例输出2】
809
Hint
【数据范围】
对于30%的数据,保证1 < =a < =b < =1000000
对于100%的数据,保证1 < =a < =b < =10000000000
显然容斥,剩下的就是黑科技的问题了。
容斥的话,就是我们首先预处理出来所有的幸运数字,然后筛一遍,筛掉所有是其他幸运数字倍数的数避免重复计算。
然后就是选1个-选2个的lcm+选3个的lcm-….
一个dfs搞定即可。
但是需要注意,dfs内部的lcm我们不可以求出来,因为会爆long long ,是的我也不知道为什么。
所以我们需要转成double 型比较是否越界。
另外,我们筛完之后的那些数字最好按照从大到小的顺序排,如果从小到大的话常数大一点点,但就是这么一点点你就过不去,貌似是从大到小的话更能够对一些非法状态早排除吧
#include
#include
#include
#include
#include
#define ll long long
#define N 10001
using namespace std;
ll l,r;
int t,n,m;
ll ans;
ll a[N],b[N];
bool vis[N];
ll gcd(ll a,ll b)
{
return b?gcd(b,a%b):a;
}
void pre(int x,ll y)
{
if(y>r)return;
if(x>0)a[++m]=y;
pre(x+1,y*10+6);
pre(x+1,y*10+8);
}
void dfs(int x,int y,ll z)
{
if(x>n)
{
if(y&1)ans+=r/z-(l-1)/z;
else if(y)ans-=r/z-(l-1)/z;
return;
}
dfs(x+1,y,z);
ll tmp=z/gcd(a[x],z);
if(((double)a[x]*tmp)<=r) dfs(x+1,y+1,a[x]*tmp);
}
int main()
{
scanf("%lld%lld",&l,&r);
pre(0,0);
sort(a+1,a+m+1);
for(int i=1;i<=m;i++)
if(!vis[i])
{
b[++n]=a[i];
for(int j=i+1;j<=m;j++)
if(!(a[j]%a[i]))
vis[j]=1;
}
for(int i=1;i<=n;i++)
a[n-i+1]=b[i];
dfs(1,0,1);
printf("%lld",ans);
}