soj3538 幸运数字 容斥原理应用

链接:soj3538

【题目大意】:

由6和8组成的数字都是lucky数字,其倍数也是lucky数字。

求给定区间[l,r]有多少个lucky数字。(1 <= l,r <= 10,000,000,000 )

【分析】:

若能求出[1,n]中有多少个lucky数字,问题即解。

先求仅有6和8组成的数字,记为primLucky数,lucky数是primLucky数的若干倍,显然有容斥原理。

Ai表示[1,n]中有多少个是primLucky[i]的倍数的数字的集合。

则由:

n|A1∪A2∪...∪Am|=∑n|Ai|1≤i≤m-∑n|Ai∩Aj|1≤i≤j≤m+∑n|Ai∩Aj∩Ak|-…+(-1)^m-1)n|A1∩A2…∩Am|1≤I,j,k≤m

写成嵌套的形式使用dfs。

生成1到10位数的primLucky数可以:

枚举1到10的长度,定长求primLucky

或者第i长的lucky是由第i-1长的lucky数+尾数为6或者8即可。

附代码:

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <ctype.h>
#include <queue>

using namespace std;

typedef long long ll;

const int maxn = 1 << 11 ;
ll lucky[maxn] ;
int len ;
const ll INF = 100000000000000000LL ;

inline ll gcd(ll a, ll b) {    return b ? gcd(b,a%b) : a ; }

ll lcm(ll a,ll b)
{
    if ( INF / a < b ){ return INF;}
    ll x = gcd(a, b);
    return a / x * b;
}

inline bool get(ll &t)
{
    bool flag = 0 ;
    char c;
    while(!isdigit(c = getchar())&&c!='-') if( c == -1 ) break ;
    if( c == -1 ) return 0 ;
    if(c=='-') flag = 1 , t = 0 ;
    else t = c ^ 48;
    while(isdigit(c = getchar()))    t = (t << 1) + (t << 3) + (c ^ 48) ;
    if(flag) t = -t ;
    return 1 ;
}

ll l , r ;

void init()
{
    ll i , j , k , t , p , q , w ;
    for( i = k = 0 ; i < 10 ; i++)
    {
        //枚举i+1长度的68数
        w = i + 1 ;
        for ( j = 0 ; j < (1<<(i+1)) ; j++)
        {
            p = 0 ; t = j ;    q = w ;
            while (q--)
            {
                p *= 10 ;
                if( t & 1 ) p += 8 ;
                else p += 6 ;
                t /= 2 ;
            }
            lucky[k++] = p ;
        }
    }
    sort(lucky,lucky+k);
    for( i = p = 1 ; i < k ; i++) 
    {
        for ( j = 0 ; j < p ; j++)
        {
            if ( lucky[i] % lucky[j] == 0 ) break ;
        }
        if( j == p ) lucky[p++] = lucky[i] ;
    }
    len = p ;
}

ll dfs(int st,ll pre,ll n)
{
    ll ret = 0 ;
    int i ;
    for ( i = 0 ; i < st ; i++)
    {
        ll w = lcm(pre, lucky[i]);
        if (w <= n) ret += n / w - dfs(i, w, n);
    }
    return ret;
}

ll work(ll n)
{
    if( n < 6 ) return 0 ;
    return dfs(len,1,n) ;
}

void solve()
{
    printf("%lld\n",work(r)-work(l));
}

int main()
{
    init();
    while (get(l))
    {
        get(r);
        l--;
        solve();
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值