1. 程式人生 > >互素,容斥原理,HDU4135 POJ2407 HDU1796

互素,容斥原理,HDU4135 POJ2407 HDU1796

(三道題目的完整程式碼在文章最後)

這幾道題都是有關互素和容斥原理的問題, 要求1~n 中與 m互質的自然數的個數的基本思路是:先找到m的所有質因數然後用容斥原理找出在1~n的範圍內與m互質的數的個數。

以HDU4135(Coprime)為例。點選看原題

該題讓我們找出在[a,b]內能被m整除的數目。求出1~m和1~n-1 的,相減即可。

首先是找出m的所有質因數:

vector<int > fac;

ll getPrimeFactor(ll n)
{
    for(int i = 2 ; i*i <= n; i++)
    {
        if(n%i == 0)
        {
            fac.push_back(i);
            while(n%i == 0) n/= i;
        }
    }
    if(n!=1)
        fac.push_back(n);
   return fac.size();
}
     容斥原理可以用DFS或者列舉子集的方法來寫,下面是DFS的寫法,每一次列舉當前的這個質因數要或者不要,最後如果看要了奇數個還是偶數個質因數,來判斷符號是加還是減(若不太理解可以百度一下容斥原理),此處是用n來除以 所有取用的質因數的乘積 得到n中能被這些質因數都整除的自然數的個數,而在其他題目中會有變化,在後面會說到。
void dfs(int dep, int flag, ll number)
{
    if(dep == num)
    {
        if(flag)
        {
            ansA -= a/number;
            ansB -= b/number;
        }
        else
        {
            ansA += a/number;
            ansB += b/number;
        }
        return;
    }

    dfs(dep+1,flag,number);
    dfs(dep+1,!flag , number*fac[dep]);
}

POJ 2407 (Relatives)點選檢視原題 此題比上一題更為簡單,直接求1~n的,我就把上一題的程式碼改了改。  HDU1796(How many integers can you find) 點選檢視原題
這題給我們一個數n和一個集合,問1~n中能被該集合中至少任意一個數整除的數有多少。 之前兩題我們也是求出一個集合(所有質因數),然後算1~n中能被該集合中的至少任意一個數整除的數有多少,所以此題方法也是容斥原理。但是,第一題中我有提到過此處有變化的地方,第一題中如果取用一個數就直接把它乘到之前所有取用的數的積上即可,而此處要取之前所有數的結果與當前取用的數的lcm,最小公倍數。因為之前的數都是質數,而此處不保證都是質數。可在程式碼中注意一下此處不同。 三道題的完整程式碼: HDU4135(Coprime)
//Chirfen 2016.8.15

//Accepted	1592 KB	15 ms

//HDU 4135

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <map>
#include <queue>
#include <vector>
#include <utility>

#define SIZE 1000010
#define ll long long

using namespace std;

ll num ,ansA, ansB, a,b,n;
vector<int > fac;

ll getPrimeFactor(ll n)
{
    for(int i = 2 ; i*i <= n; i++)
    {
        if(n%i == 0)
        {
            fac.push_back(i);
            while(n%i == 0) n/= i;
        }
    }
    if(n!=1)
        fac.push_back(n);
   return fac.size();
}

void dfs(int dep, int flag, ll number)
{
    if(dep == num)
    {
        if(flag)
        {
            ansA -= a/number;
            ansB -= b/number;
        }
        else
        {
            ansA += a/number;
            ansB += b/number;
        }
        return;
    }

    dfs(dep+1,flag,number);
    dfs(dep+1,!flag , number*fac[dep]);
}

int main()
{
    int T ; cin >> T;
    int _case = 1;
    while(T--)
    {
        fac.clear();
        scanf("%I64d %I64d %I64d",&a,&b,&n);
        a--;
        num = getPrimeFactor(n);
        ansA = 0 ; ansB = 0;
        dfs(0,0,1);
        printf("Case #%d: %I64d\n",_case++, ansB - ansA);
    }
    //cout << "Hello world!" << endl;
    return 0;
}


POJ 2407 (Relatives)點選檢視原題
//Chirfen 2016.8.15

//Accepted	692 KB	16 ms


//POJ 2407

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <map>
#include <queue>
#include <vector>
#include <utility>

#define SIZE 1000010
#define ll long long

using namespace std;

ll num , ans, n;
vector<int > fac;

ll getPrimeFactor(ll n)
{
    for(int i = 2 ; i*i <= n; i++)
    {
        if(n%i == 0)
        {
            fac.push_back(i);
            while(n%i == 0) n/= i;
        }
    }
    if(n!=1)
        fac.push_back(n);
   return fac.size();
}

void dfs(int dep, int flag, ll number)
{
    if(dep == num)
    {
        if(flag)
        {
            ans -= n/number;
        }
        else
        {
            ans += n/number;
        }
        return;
    }

    dfs(dep+1,flag,number);
    dfs(dep+1,!flag , number*fac[dep]);
}

int main()
{

    while(scanf("%I64d",&n) && n)
    {
        fac.clear();
        num = getPrimeFactor(n);
        ans = 0;
        dfs(0,0,1);
        printf("%I64d\n", ans);
    }
    //cout << "Hello world!" << endl;
    return 0;
}




 HDU1796(How many integers can you find) 點選檢視原題
//Chirfen 2016.8.15

//Accepted	1576 KB	93 ms

//

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <map>
#include <queue>
#include <vector>
#include <utility>

#define SIZE 1000010
#define ll long long

using namespace std;

ll num , ans, n , m;
vector<int > fac;

int gcd(int n, int m)
{
    if(m==0)
        return n;
    return gcd(m,n%m);
}

int lcm(int n,int m)
{
    return n/gcd(n,m)*m;
}

void dfs(int dep, int flag, ll number)
{
    if(dep == num)
    {
        if(flag)
        {
            ans -= n/number;
        }
        else
        {
            ans += n/number;
        }
        return;
    }
    dfs(dep+1,flag,number);
    dfs(dep+1,!flag , lcm(number,fac[dep]));
}

int main()
{

    while(~scanf("%I64d %I64d",&n, &m))
    {
        n--;
        fac.clear();
        int temp;
        int flag = 0;
        for(int i = 0 ; i <m ;i++)
        {
            scanf("%d", &temp);
            if(temp == 1)  flag = 1;
            if(temp != 0)  fac.push_back(temp);
        }

        if(flag)
        {
            printf("%d\n",n);
            continue;
        }
        num = fac.size();
        ans = 0;
        dfs(0,0,1);
        printf("%d\n", n-ans);
    }
    //cout << "Hello world!" << endl;
    return 0;
}


 HDU1796(How many integers can you find) 點選檢視原題