「BZOJ1853」[SCOI2010] 幸运数字(容斥原理)

题目链接:传送门

题目大意:

幸运数的定义:只包含6或8

近似幸运数:幸运数的倍数

问给定一个区间内近似幸运数的个数

题目思路:

我们从简单的开始思考,求100以内能被6,8,10整除的数的个数。

(100/6+100/8+100/10)-(100/24+100/30+100/40)+100/120

就是简单的容斥原理。

同样这道题也是,预处理出所有比他小幸运数之后容斥即可。

有几个点需要注意:

1.幸运数中可以删去既是幸运数也是近似幸运数的数,这种数是重复的

2.

此题数据范围是真正的10^10,于是我们需要一些方法来防止爆long long

就是这段if(((double)a[x]*tmp)<=r) dfs(x+1,y+1,a[x]*tmp);

因为a[x]*tmp可能超过long long,于是我们先用double存它的近似值,如果它小于r,则一定在long long范围内,就可以直接乘了

#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int maxn=50000;
typedef long long ll;
ll a[maxn];
int cnt;
void dfs1(int dpth,ll s)
{
    if(dpth==10)return;
    a[cnt++]=s*10+6;
    dfs1(dpth+1,s*10+6);
    a[cnt++]=s*10+8;
    dfs1(dpth+1,s*10+8);
}
int num;
ll b[maxn];
ll gcd(ll a,ll b)
{
    if(b==0)return a;
    return gcd(b,a%b);
}
ll l,r;
ll ans=0;
void dfs(int x,int y,ll z)
{
	if(x>=num)
	{
		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(b[x],z);
	if(((double)b[x]*(double)tmp)<=r)
	   dfs(x+1,y+1,b[x]*tmp);
}
bool cmp(ll a,ll b)
{
    return a>b;
}
int main()
{
    cnt=0;
    dfs1(0,0);
    sort(a,a+cnt);
    num=0;
    for(int i=0;i<cnt;i++)
    {
        int ok=true;
        for(int j=0;j<i;j++)
        {
            if(a[i]%a[j]==0)
            {
                ok=false;
                break;
            }
        }
        if(ok)b[num++]=a[i];
    }
    sort(b,b+num,cmp);
    while(~scanf("%lld%lld",&l,&r))
    {
        ans=0;
        dfs(0,0,1);
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36782366/article/details/81513753