HDU 1796How many integers can you find(容斥原理+深度优先搜索)

How many integers can you find

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

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

原理:首先考虑一个问题,1000以内6,7,8,9的倍数有多少个?答案是

1000div6+1000div7+1000div8+1000div9

-1000div(6*7)-1000div(6*8)-1000div(6*9)-1000div(7*8)-1000div(7*9)-1000div(8*9)

+1000div(6*7*8)+1000div(6*8*9)+1000div(7*8*9)+1000div(6*7*9)

-1000div(6*7*8*9)
以一个数6为例说一下下面的深搜思想
6,lcm=1,k=1->lcm=6,k=2,ans+=6;dfs(2,6,2)->ans-=42,lcm=42,k=3;接着深搜

这是容斥原理的一个最简单的应用,类比这道题,Step3到4其实将每个数a的不重复约数记录了下来,有公共约数的四个数的方案要从ans中减去,多减的要加上

奇加偶减
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<ctype.h>
#include<algorithm>
#include<string>
#define PI acos(-1.0)
#define maxn 15
#define INF 1<<25
typedef long long ll;
using namespace std;
ll n,m,ans=0,num[260],tot;
ll gcd(ll a,ll b)
{
    return b==0?a:gcd(b,a%b);
}
void dfs(ll pos,ll pre_lcm,ll k)
{
    for(ll i=pos;i<=tot;i++)
    {
        int lcm=pre_lcm/gcd(num[i],pre_lcm)*num[i];//求出他与pre_lcm的最小公倍数(利用最大公约数求)
        if(k&1)ans+=(n-1)/lcm;
//若k是奇数,则加;偶数则减,称为奇加偶减。
        else ans-=(n-1)/lcm;
        dfs(i+1,lcm,k+1);//深搜下一个数,lcm更新,k加一,以上面的一组数字为例。
    }
}
int main()
{
    ll x,i,j;
    while(scanf("%lld %lld",&n,&m)!=EOF)
    {
        memset(num,0,sizeof(num));
        ans=0;
        tot=0;
        for(i=1;i<=m;i++)
        {
            cin>>x;
            if(x>0&&x<n)
            {
                num[++tot]=x;
//先筛选出在0与n之间的数,这样的数才符合有可能被比n小的数整除。
            }
        }
        dfs(1,1,1);//深度优先搜索,从第一个数开始,pre_lcm=1,k=1;k代表这一个数与其他数重叠次数。
        cout<<ans<<endl;
    }
}

猜你喜欢

转载自blog.csdn.net/lanshan1111/article/details/81624833