zoukankan      html  css  js  c++  java
  • ZOJ 3233 Lucky Number --容斥原理

    这题被出题人给活活坑了,题目居然理解错了。。哎,不想多说。

    题意:给两组数,A组为幸运基数,B组为不幸运的基数,问在[low,high]区间内有多少个数:至少被A组中一个数整除,并且被B中任意一个数整除。|A|<=15.

    分析:看到A长度这么小,以及求区间内满足条件的个数问题,容易想到容斥原理,因为不被B中任意一个数整除,所以将B数组所有数取一个最小公倍数LCM,那么就变成了幸运数字都不会被这个LCM整除。

    然后枚举子集,实现要将A中元素去除相互整除的情况,比如A = [2,4],这时因为被至少一个数整除就行,那么一个2就可以满足了,将4去掉。

    设A1 = {区间内被a1整除的数},A2 = {区间内被a2整除的数},...An = {区间内被an整除的数}

    那么由于:

    然后还要处理不被LCM整除的情况,设Bi = {区间内被 i 整除的数},则要减去的数为: 

    (cnt为数的个数)

    所以就可以做容斥了。

    一段区间[low,high]内被k整除的数的个数为: high/k-(low-1)/k

    注意处理爆long long的情况

    代码: (0ms)

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #define ll long long
    using namespace std;
    #define N 100007
    
    ll a[17],aa[17];
    ll b[504];
    int vis[17];
    
    ll gcd(ll a,ll b)
    {
        if(!b)
            return a;
        return gcd(b,a%b);
    }
    
    int calc(int S)  //计算数的个数
    {
        int cnt = 0;
        while(S)
        {
            if(S&1)
                cnt++;
            S >>= 1;
        }
        return cnt;
    }
    
    int main()
    {
        int n,m,i,j;
        ll low,high;
        ll cnt;
        int S;
        while(scanf("%d%d%lld%lld",&n,&m,&low,&high)!=EOF)
        {
            if(n == 0 && m == 0 && low == 0 && high == 0)
                break;
            for(i=0;i<n;i++)
                scanf("%lld",&a[i]);
            for(i=0;i<m;i++)
                scanf("%lld",&b[i]);
            memset(vis,0,sizeof(vis));
            sort(a,a+n);
            for(i=0;i<n;i++)
            {
                if(vis[i])
                    continue;
                for(j=i+1;j<n;j++)
                {
                    if(vis[j])
                        continue;
                    if(a[j]%a[i] == 0)   //去除冗余
                        vis[j] = 1;
                }
            }
            int ka = 0;
            for(i=0;i<n;i++)
            {
                if(!vis[i])
                    aa[ka++] = a[i];
            }
            ll lcm = 1LL;
            int tag = 1;
            for(i=0;i<m;i++)  //求LCM
            {
                lcm = lcm*b[i]/(gcd(lcm,b[i]));
                if(lcm > high || lcm < 0)
                {
                    tag = 0;
                    break;
                }
            }
            if(!tag)   //爆出,不管
                lcm = high+1;
            S = (1<<ka)-1;   //总状态数
            ll ans = 0;
            for(int state=1;state<=S;state++)
            {
                int c = calc(state);
                int sign = (c%2?1LL:-1LL); //根据个数定符号
                int tmp = state;
                i = 0;
                ll antibase = 1LL;
                ll base = 1LL;
                int flag = 1;
                while(i<ka)   //子集
                {
                    if(tmp&1)
                    {
                        base = base/gcd(base,aa[i])*aa[i];
                        if(base > high || base < 0)
                        {
                            flag = 0;
                            break;
                        }
                    }
                    tmp>>=1;
                    i++;
                }
                if(!flag)
                    continue;
                ans += sign*(high/base-(low-1LL)/base);
                if(!tag)   //LCM爆范围,不管
                    continue;
                antibase = base/(gcd(base,lcm))*lcm;
                ans -= sign*(high/antibase-(low-1LL)/antibase);
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
    View Code

    (有发现不对的地方欢迎评论指出)

  • 相关阅读:
    php命令注入
    mysql事物
    安装php环境
    移除服务器缓存实例
    show user profile synchronization tools
    manual start user profile import
    JSON is undefined. Infopath Form People Picker in SharePoint 2013
    asp.net web 应用站点支持域账户登录
    Load sharepoint envirement by powershell
    sharepoint 2016 download
  • 原文地址:https://www.cnblogs.com/whatbeg/p/3870497.html
Copyright © 2011-2022 走看看