关于数的容斥定理的代码实现

来源:互联网 发布:软件配置管理招聘 编辑:程序博客网 时间:2024/04/28 18:15

  前述:其实容斥定理的用法并非自己感悟出来的,还是从大量博客里学习的,感觉这种深邃的思想我学不来,看题目的时候都想不到为什么要那样用,但是容斥定理本身是很简单的,公式我就不再展示了。但是还好因为集合的交集的求法比较困难,使得这方面能用固定代码解决的问题的种类大大减少。其中比较广泛的一个应用就是求1-m里面和n互质的数的个数。思路是用m-所有和n非互质的数的和,那么,和n非互质又有一个说法就是和n有大于1的公共素因子,而属于有某个因子a的数的个数的集合个数就是m/a。这样,问题就转化成了这样几个步骤:1.先求n的所有素因子。2.根据容斥定理求和n非互质的数的个数。3.ans=m-和n非互质的数的个数。那么这里我为啥说是代码实现而不说是模板呢,在dalao的博客里面我发现的写法要么是难懂的位运算,要么不用递归难想,而我选择了最直接也可能是最耗时间的解决方法,就是dfs递归,一开始只是出于试探的心理,后来交了几个题目之后发现并未出现超时的现象,于是就当作一个模板来用了,也做出来不少容斥定理的题目。

  AC代码:(参见题目:HDU-4135 J - Co-prime )

  题目大意:让求一个区间里面和n互质的数的个数。

  解题思路:假设为[l,r],那么有可以用1-r满足条件的数的个数1-l-1中满足条件的数的个数。

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
long long fac[1100];
long long box[1100];
long long n,m,t;
long long fun;
long long gcd(long long a,long long b){
    return b==0?a:gcd(b,a%b);
}
void getfac(){
    long long i,j;
    long long xx=n;
    m=0;
    for (i=2;i<=sqrt(n);i++){
        if (xx%i==0){
            fac[++m]=i;
            while (xx%i==0){
                xx/=i;
            }
        }
    }
    if (xx>1)fac[++m]=xx;
}
void sfind(long long x,long long y,long long z,long long val){
    long long i,j;
    for (i=x;i<=m;i++){
        box[y]=fac[i];
        if (y==z){
            long long temp=1;
            for (j=1;j<=z;j++){
                temp=temp/gcd(temp,box[j])*box[j];
            }
            fun+=val/temp;
        }
        else sfind(i+1,y+1,z,val);
    }
}
long long solve(long long x){
    long long i,j;
    long long temp=1;
    long long ans=0;
    for (i=1;i<=m;i++){
        fun=0;
        sfind(1,1,i,x);
        ans+=fun*temp;
        temp=-temp;
    }
    return x-ans;
}
int main(){
    long long i,j,k,l,r,total,cas=0;
    scanf("%lld",&t);
    while (t--){
        scanf("%lld%lld%lld",&l,&r,&n);
        getfac();
        long long ans=solve(r)-solve(l-1);
        printf("Case #%lld: %lld\n",++cas,ans);
    }
}

  这个题目其实不是我创造出来这个写法的初始版本,这里改造成了一个函数,可以更为广泛的应用。

原创粉丝点击