互素,容斥原理,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)  点击查看原题
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值