1. 程式人生 > >[luogu2576 SCOI2010] 幸運數字 (容斥原理)

[luogu2576 SCOI2010] 幸運數字 (容斥原理)

www. 傳送門 long long org += 容斥 但是 scrip include

傳送門

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 10

Sample Output

2

HINT

對於30%的數據,保證1<=a<=b<=1000000

對於100%的數據,保證1<=a<=b<=10000000000

Solution

預處理幸運號碼(極少),去掉其中互為倍數的
dfs枚舉其中的數,求lcm用邊界除一下就得到倍數個數
考慮容斥,算完即可

Code

//By Menteur_Hxy
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define F(i,a,b) for(register int i=(a);i<=(b);i++)
using namespace std;
typedef long long LL;

const int N=3010;
bool vis[N];
int n,m;
LL l,r,ans;
LL a[N],b[N];

void pre(LL x) {
    if(x>r) return ;
    if(x) a[++n]=x;
    pre(x*10+6);pre(x*10+8);
}

LL gcd(LL a,LL b) {return !b?a:gcd(b,a%b);}
void dfs(int x,int y,LL z) {
    if(x>m) {
        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]<=(double)r/tmp) //防爆類型
        dfs(x+1,y+1,a[x]*tmp);
}

int main() {
    scanf("%lld%lld",&l,&r);
    pre(0); sort(a+1,a+1+n);
    F(i,1,n) if(!vis[i]) {
        b[++m]=a[i];
        F(j,i+1,n) if(a[j]%a[i]==0) vis[j]=1;
    }
    F(i,1,m) a[i]=b[m-i+1];
    dfs(1,0,1);
    printf("%lld",ans);
    return 0;   
} 

[luogu2576 SCOI2010] 幸運數字 (容斥原理)