hdu 1796 容斥原理 + longlong

题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=1796

How many integers can you find

Time Limit: 12000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 3539    Accepted Submission(s): 994


Problem Description
  Now you get a number N, and a M-integers set, you should find out how many integers which are small than N, that they can divided exactly by any integers in the set. For example, N=12, and M-integer set is {2,3}, so there is another set {2,3,4,6,8,9,10}, all the integers of the set can be divided exactly by 2 or 3. As a result, you just output the number 7.
 

 

Input
  There are a lot of cases. For each case, the first line contains two integers N and M. The follow line contains the M integers, and all of them are different from each other. 0<N<2^31,0<M<=10, and the M integer are non-negative and won’t exceed 20.
 

 

Output
  For each case, output the number.
 

 

Sample Input
12 2 2 3
 

 

Sample Output
7
 

 分析:

1:基本的容斥原理。给定一个N 和一个M集合, 找到小于n 的数中有多少是M中元素的整数倍的  个数。 S= n-1

设xi 为 S 中能被 M 中 i 个数整除的个数 则 Xans = x1 - x2 + x3 -……+(-1)^(m-1)xm。

需要注意的是 , 求能被i 个数整除的 个数的时候, 用(n-1)/ lcm(mi)  , 即除以M集合中元素的最小公倍数。

 2: 从组合数学上分析

设全集 为 S={1 2 3 , ... , n-1}  |s| = n-1;

M = {m1, m2 , ...  , mm}, 且 M中 个元素互素

令Qi(i=12, ,,, , m) 表示S中 能被 mi整除的整数这一性质, 令Ai 为S中 具有性质Qi的那些整数所组成的集合。则|A1 υ A2 υ ...υ  Am| 表示S中能被任意mi整除的整数个数。

|S| -  |A1 υ A2 υ ...υ  Am|  表示 S中 不能被 mi (i = 1, 2 ,...,m) 整除的整数的个数。即S中 与 M 互质的个数。

又    |A1 υ A2 υ ...υ  Am|  = ∑|Ai| - ∑|Ai ∩ Aj|  + ∑|Ai ∩ Aj ∩ Ak| -...+(-1)^(m-1)|A1 ∩ A2 ∩... ∩Am|

又    |Ai| = |S| / (mi)     |Ai ∩ Aj|  = |S| / (mi * mj)   同理

注意:如果M中各个元素不是 互素的 ,则 |Ai| = |S| / (mi)  ,   |Ai ∩ Aj|  = |S| / lcm(mi , mj)    ,正是本题题意。

3:容斥原理 ,是采用 dfs 搜索, 分别对 M 中的 每个元素mi 枚举,  然后遍历mi ,搜索树,设树根深度为1,  当树深度为偶数,sum-= , 当 树深度为 奇数时 ,sum+=。

递归图如下:

本题代码如下:

#include<iostream>
#include<stdlib.h>
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<string>
#include<queue>
#include<algorithm>
#include<map>

using namespace std;
typedef long long LL;   // 大数 _int64 ,大数输出 // %I64d 
int n, M;
int a[22];    // 存储的是M中的值
LL gcd(LL x, LL  y)   //最大公约数
{
    return y==0?x:gcd(y,x%y);
}
LL lcm(LL x, LL y )    //  最小公倍数
{
    return x*y/gcd(x,y);
}
//         当前点,  搜索树的深度, 记录容斥过程值
void dfs(int now, int count , LL lcm_1 , LL &sum)
{
    lcm_1 = lcm(a[now], lcm_1);
    if(count&1)
        sum+=(n-1) / lcm_1;
     else sum-=(n-1) / lcm_1;
     for(int i=now+1 ; i<M ;i++)
        dfs(i,count+1 , lcm_1, sum);    // 搜索方向
}
int main()
{
    int b, len;
    LL sum;
    while(scanf("%d%d",&n,&M)!= EOF)
    {
        sum=0;
        len=0;
        for(int i=0; i<M ; i++)
        {
            scanf("%d",&b);
            if(b)
                a[len++]= b;
        }
        M=len;
        for( int i=0;i<M;i++)    // 枚举M中集合mi
            dfs(i , 1 ,a[i], sum);
        printf("%I64d\n", sum);

    }
    return 0;
}

 

 

转载于:https://www.cnblogs.com/zn505119020/p/3596279.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值