LightOJ - 1117 Helping Cicada (求1~n有多少个数不能被这m个数中任意一个整除)(容斥+状态压缩)

Posted 老板,来一盆泪流满面

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LightOJ - 1117 Helping Cicada (求1~n有多少个数不能被这m个数中任意一个整除)(容斥+状态压缩)相关的知识,希望对你有一定的参考价值。

题意:http://www.lightoj.com/volume_showproblem.php?problem=1117

考虑1个数k,1~n有[n/k]个数能被k整除,[a]表示a向下取整,

所以ans= n-SIGMA([n/num[i]])(1<=i<=m)。再考虑2个数a,b,因为被a整除同时被b整除这部分减了两次,

所以要加上,ans += n/lcm(a,b),枚举2个数,又发现3个数的多加了,再减去3个的,再加上4个的,减去5个的,以此类推。

比如m=4

我们就有2的4次方减一 也就是15种方法   可以为二进制的1111 正好对应这m  所以将每一个都便利

然后就是奇数加  偶数减   求出m个数在【1-n】中有多少个倍数

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<map>
#include<vector>
#include<math.h>
#include<string>
using namespace std;
#define INF 0x3f3f3f3f
#define LL long long
#define N 106
#define Lson rood<<1
#define Rson rood<<1|1
int a[N];
LL gcd(LL a,LL b)
{
    return b==0?a:gcd(b,a%b);
}
int main()
{
    int T,t=1;
    scanf("%d",&T);
    while(T--)
    {
        LL n,m,sum=0,ans=0;
        scanf("%lld%lld",&n,&m);
        for(int i=0;i<m;i++)///状态压缩
            scanf("%lld",&a[i]);
        for(int i=1;i<(1<<m);i++)
        {
            LL ans=1;
            int t=0;///将为一种情况都遍历出来
            for(int j=0;(1<<j)<=i;j++)
            {
                if(1<<j&i)
                {
                    t++;
                    ans=ans*a[j]/gcd(ans,a[j]);
                }
            }///根据公式  奇数加  偶数减
            if(t%2) sum+=n/ans;
            else sum-=n/ans;
        }///sum保存的是在n中有多少个(m个数的倍数)
        printf("Case %d: %lld\n",t++,n-sum);
    }
    return 0;
}

 

  

以上是关于LightOJ - 1117 Helping Cicada (求1~n有多少个数不能被这m个数中任意一个整除)(容斥+状态压缩)的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces #282 div 1 C Helping People 题解

CF494C Helping People

CF1105E Helping Hiasat

E - Helping the HR Gym - 102040E

[CF494C]Helping People

CF1105E Helping Hiasat