UVA-10325 The Lottery(二進制狀態枚舉)

The Sports Association of Bangladesh is in great problem with their latest lottery ‘Jodi laiga Jai’. There are so many participants this time that they cannot manage all the numbers. In an urgent meeting they have decided that they will ignore some numbers. But how they will choose those unlucky numbers!!! Mr. NondoDulal who is very interested about historic problems proposed a scheme to get free from this problem

his problem. You may be interested to know how he has got this scheme. Recently he has read the Joseph’s problem.

There are N tickets which are numbered from 1 to N. Mr. Nondo will choose M random numbers and then he will select those numbers which is divisible by at least one of those M numbers. The numbers which are not divisible by any of those M numbers will be considered for the lottery

As you know each number is divisible by 1. So Mr. Nondo will never select 1 as one of those M numbers. Now given N, M and M random numbers, you have to find out the number of tickets which will be considered for the lottery.

Input

Each input set starts with two Integers N (10 ≤ N < 2 31) and M (1 ≤ M ≤ 15). The next line will contain M positive integers each of which is not greater than N.

Input is terminated by EOF.

Output

Just print in a line out of N tickets how many will be considered for the lottery.

Sample Input

10 2

2 3

20 2

2 4

Sample Output

3

10

題目意思:

      給你m個整數,求1~n中不能被m個整數中任意一個整除的數有多少個

解題思路:

      由於對1~n中不能被m個數字中任意一個數整除的數不好求(時間複雜度太高),我們可以通過計算在1~n中不能被m個數字中任意一個數整除的數的個數爲n-能被m個數整除的數的個數

      1到n,能被m中數字a整除的個數有n / a個,能被a 、b同時整除的數有n / lcm(a,b)個,能被a、b、c同時整除的數有n / lcm(lcm(a,b),c)個......由於計算過程中能被一個數整除的數的個數,我們需要使用容斥原理。不能被m個數字中任意一個數整除的個數=n-能被一個數整除的個數+能同時被兩個數字整除的個數-能同時被三個數字整除的個數.......

方法一:

      DFS:依次對m個數字中的每個數進行DFS,每個數都可能與其他數同時被1~n之間的數整除

#include<iostream>
#include<cstring>

using namespace std;

typedef long long LL;

LL arr[20],n,m,res;

LL GCD(LL a,LL b) {
    if(a < b) {
        a = a + b;
        b = a - b;
        a = a - b;
    }
    LL c = a % b;
    while(c) {
        a = b;
        b = c;
        c = a % b;
    }
    return b;
}

LL LCM(LL a,LL b) {
    return (a / GCD(a,b)) * b;
}

void DFS(LL part,LL loc,LL num) {
    if(part > n || loc >= m) {
        return ;
    }
    for(LL i = loc;i < m;i++) {
        LL tmp = LCM(part,arr[i]);
        if(num & 1)
            res -= n / tmp;
        else
            res += n / tmp;
        DFS(tmp,i+1,num+1);
    }
}

int main() {
    while(~scanf("%lld%lld",&n,&m)) {
        res = n;
        for(int i = 0;i < m;i++) {
            scanf("%lld",&arr[i]);
        }
        for(int i = 0;i < m;i++) {
            res -= n / arr[i];
            DFS(arr[i],i+1,2);
        }
        printf("%lld\n",res);
    }
    return 0;
}

 方法二:

      二進制狀態枚舉:對於m個數我們可以使用m位二進制即2^m種情況來枚舉所有的取值情況(1爲取,0爲不取)

      如2個數字可以由00(兩個數都不能被整除)   01(能被第一個數整除,不能被第二個數整除)   10(能被第二個數整除,不能被第一個數整除)   11(能同時被第一個數和第二個數整除)四種取值情況,由於我們要求能被m個數中的數整除的個數,因此不考慮一個數都不取的狀況,因此我們從01的狀態開始枚舉。part爲之前的tmp個數的最小公倍數,當tmp爲奇數時,res-=n/tmp,當tmp爲偶數時res+=n/tmp

#include<bits/stdc++.h>

using namespace std;

typedef long long LL;


LL GCD(LL a, LL b) {
    if(a < b) {
        a = a + b;
        b = a - b;
        a = a - b;
    }
    LL c = a % b;
    while(c) {
        a = b;
        b = c;
        c = a % b;
    }
    return b;
}

LL LCM(LL a, LL b) {
    return a / GCD(a,b) * b;
}

int main() {
    LL n, m, res, tmp, part, arr[20];
    while(cin >> n >> m) {
        for(int i = 0;i < m;i++)
            cin >> arr[i];
        res = n;
        for(int i = 1;i < (1 << m);i++) {       //枚舉arr數組中每個數的狀態(出現爲1,否則爲0)
            tmp = 0;                            //記錄當前情況下是幾個數字的最小公倍數
            part = 1;
            for(int j = 0;j < m;j++) {
                if(i & (1 << j)) {              //若在當前狀態下整數arr[j]的狀態爲被取到,則計算當前狀態下的LCM
                    part = LCM(part,arr[j]);
                    tmp++;
                }
            }
            if(tmp & 1)                         //若取的數字爲奇數
                res -= n / part;
            else
                res += n / part;
        }
        cout << res << endl;
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章