bzoj1853: [Scoi2010]幸运数字(容斥原理)

本文介绍了一种解决特定数学问题的方法:计算两个整数区间内所有幸运数字的个数。通过对幸运数字进行筛选并利用最小公倍数和递归搜索算法优化计算过程,实现了高效的求解方案。

题目传送门
这道题。。
数据有点强啊。。。。

解法:
我好垃圾我一开始的想法如下:
先把幸运数字都求出来。。
然后用容斥原理去搜。。
奇数个的时候就加上答案,偶数个的时候就减去答案。

结果。。
数据规模十的十次方。
那么有两千个幸运数字诶。
容斥原理2的两千次方。
T到死。。。。。

想了很久,从大到小会不会常数稍微小一点呢。。
(以前做过那种爆搜填木桶的题,从大到小可以AC从小到大要T)
然后打了个代码。
T了。
T了很多次。。
找不出原因啊上网%题解呀。。。。
发现还有这种操作?!
dfs里面求最小公倍数会炸。。。
要把它转成double类型然后再判断?!
不懂,求神犇指教。

代码实现:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
ll st,ed;int len1,len2;
ll a[11000],b[11000];
ll s[11000];int len;
void dfs2(ll x) {if(x>ed)return ;if(x!=0)a[++len1]=x;dfs2(x*10+6);dfs2(x*10+8);}
bool cmp(ll n1,ll n2) {return n1<n2;}
bool cmp1(ll n1,ll n2) {return n1>n2;}
ll get_sum(ll x) {return ed/x-(st-1)/x;}
ll gcd(ll x,ll y) {if(x==0)return y;return gcd(y%x,x);}
ll lcu(ll x,ll y) {if(x>y)swap(x,y);return x/gcd(x,y)*y;}
ll ans=0;bool v[11000];
void dfs(ll Gcd,int x,int k) {
    if(x==len+1) {
        if(k==0)return ;
        if(k%2==1) ans+=get_sum(Gcd);
        else ans-=get_sum(Gcd);
        return ;
    }
    dfs(Gcd,x+1,k);
    ll t=Gcd/gcd(Gcd,s[x]);
    if((double)t*s[x]<=ed)dfs(lcu(s[x],Gcd),x+1,k+1);
}
int main() {
    scanf("%lld%lld",&st,&ed);
    len1=0;dfs2(0);
    sort(a+1,a+1+len1,cmp);
    memset(v,true,sizeof(v));
    for(int i=1;i<=len1;i++)if(v[i]==true)for(int j=i+1;j<=len1;j++)if(a[j]%a[i]==0)v[j]=false;   //如果大的包含小的那我们肯定保留小的呀,因为小的迟早会覆盖到大的。
    len=0;for(int i=1;i<=len1;i++)if(v[i]==true)s[++len]=a[i];
    sort(s+1,s+1+len,cmp1);
    ans=0;dfs(1,1,0);printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值