zoukankan      html  css  js  c++  java
  • gym 101982 B题 Coprime Integers

    题目链接:https://codeforces.com/gym/101982/attachments

    贴一张图吧:

    题目意思就是给出四个数字,a,b,c,d,分别代表两个区间[a,b],[c,d],从这两个区间里面分别拿一个数字组成(x,y),问x和y互质的组合有多少种。

    这道题目好像要用莫比乌斯反演,但是目前没有了解过这个知识点,后续会补上,我用的是打表+容斥定理做的,相比于上一种方法,耗费的时间可能会多很多。我亲测是600到800ms,所以还是很有必要学莫比乌斯反演的。

    接下来讲我的思路:两个区间里面所有的组合数是(b-a+1)*(d-c+1)种,我可以先算出不互质的组合的个数,再用总数减去它得到互质的组合数。

    首先,假设我要算所有gcd(x,y)=2的组合数,那么在区间[a,b]里面,素因子含有2的数字个数是b/2-(a-1)/2这么多个,在区间[c,d]里面含有2这个素因子的数字的个数是d/2-(c-1)/2这么多。这两个数字相乘就是两个区间中gcd(x,y)=2的组合数字。

    假如我们遍历计算1到10000000里面所有的素数(大概660000多一点),那么就会出现重复计算的情况,假如我gcd(x,y)=2和gcd(x,y)=3的情况都计算了一边,那么gcd(x,y)=6的情况就计算了两遍,那么我们就要再减去gcd(x,y)=6的情况的组合数。

    这就要用到容斥定理(奇加偶减),假如一个数字n,它不同的素因子有奇数个,那么就加,如果是偶数个就减,并且它某一个素因子个数不能大于1个(6=2*3,它的素因子有2和3,素因子2有且只有一个,素因子3有且只有一个,那么这个数字我们是要计算的,另一个数字12=2*2*3,它的素因子2有2个,那么我们就不用计算它,因为它已经包含在(gcd(x,y)=2)的数量+(gcd(x,y)=3)的数量-(gcd(x,y)=6)里面了)。

    那么我们现在就要先打表把所有类似于6(2*3),10(2*5),30(2*3*5),这种相同素因子只有一个的数筛出来(大概6000000个,所以花费时间有点多),然后遍历计算就可以了。

    这个打表的过程可以在我们线性筛素数的过程中做到,所以这个打表是线性的。

    这里面num[i]代表数字i有多少个不同的素数,例如num[30]=3,(30=2*3*5)。

    flag[i]表示数字i是不是所有素数有且只有一个,如果flag[i]=true,那么这个i就是我们要找的数字。数组ok就是把这些数字存起来,等下遍历数组ok就可以了。

    打表代码:

    void init(){
        memset(vis,0,sizeof(vis));
        memset(flag,false,sizeof(flag));
        cnt=0;//记录素数个数 
        cc=0;//计录我们要找的数组个数 
        for(int i=2;i<maxn;i++){
            if(vis[i]==0){
                prime[cnt++]=i;//是一个素数 
                num[i]=1;       //不同的素因子是有它自己一个,复制为1 
                ok[cc++]=i;       //保存在ok数组中 
                flag[i]=true;  //标记这个数字是我们要找的 
            }
            for(int j=0;j<cnt&&(i*prime[j]<maxn);j++){
                vis[prime[j]*i]=true;
                if((i%prime[j])!=0)//在这之前我们已经知道了num[i],只要i不被prime[j]整除,那么prime[j]*i这个数字不同素因子个数就是num[i]+1 
                num[prime[j]*i]=num[i]+1;
                if(flag[i]==1&&(i%prime[j])){//假如flag[i]=true,说明i是我们要找的数字,并且i%prime[j]非0,那么prime[j]*i也是我们要找的数字 
                    ok[cc++]=i*prime[j];
                    flag[i*prime[j]]=true;
                }
                if(i%prime[j]==0)
                break;
            }
        }
    }

    完整代码:

    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<map>
    #include<stack>
    #include<cmath>
    #include<vector>
    #include<set>
    #include<cstdio>
    #include<string>
    #include<deque> 
    using namespace std;
    typedef long long LL;
    #define eps 1e-8
    #define INF 0x3f3f3f3f
    #define maxn 10000005
    int prime[maxn],vis[maxn],num[maxn],ok[maxn],flag[maxn];
    int n,m,k,t,cnt,cc;
    void init(){
        memset(vis,0,sizeof(vis));
        memset(flag,false,sizeof(flag));
        cnt=0;//记录素数个数 
        cc=0;//计录我们要找的数组个数 
        for(int i=2;i<maxn;i++){
            if(vis[i]==0){
                prime[cnt++]=i;//是一个素数 
                num[i]=1;       //不同的素因子是有它自己一个,复制为1 
                ok[cc++]=i;       //保存在ok数组中 
                flag[i]=true;  //标记这个数字是我们要找的 
            }
            for(int j=0;j<cnt&&(i*prime[j]<maxn);j++){
                vis[prime[j]*i]=true;
                if((i%prime[j])!=0)//在这之前我们已经知道了num[i],只要i不被prime[j]整除,那么prime[j]*i这个数字不同素因子个数就是num[i]+1 
                num[prime[j]*i]=num[i]+1;
                if(flag[i]==1&&(i%prime[j])){//假如flag[i]=true,说明i是我们要找的数字,并且i%prime[j]非0,那么prime[j]*i也是我们要找的数字 
                    ok[cc++]=i*prime[j];
                    flag[i*prime[j]]=true;
                }
                if(i%prime[j]==0)
                break;
            }
        }
    }
    int main()
    {
        int a,b,c,d;
        init();
        sort(ok,ok+cc);
        while(scanf("%d%d%d%d",&a,&b,&c,&d)!=EOF){
            LL ans=0;
            int maxx=min(b,d);//记录一下两个,区间最小的右边界,可有可无吧 ,好像影响不大 
            for(int i=0;i<cc&&ok[i]<=maxx;i++){
                int now=ok[i];
                if(num[now]%2){//计数加 ,注意答案非常大,要用long long 
                    ans+=(LL)(b/now-(a-1)/now)*(d/now-(c-1)/now);
                }else{//偶数减 
                    ans-=(LL)(b/now-(a-1)/now)*(d/now-(c-1)/now);
                }
            }
            printf("%lld
    ",(LL)(b-a+1)*(d-c+1)-ans);
        }
        return 0;
    }

    待补充。。。。。。

    来补充了,额,请看下面大佬介绍莫比乌斯反演,完......

    补充:https://www.cnblogs.com/chenyang920/p/4811995.html

  • 相关阅读:
    MYSQL show engine innodb status 这么多年,你真的都懂?
    Python pymongo 中文乱码问题
    Python 进程与进程池
    MongoDB SyntaxError: Non-ASCII character 'xe4' in file test1.py on line 8, but no encoding declared;
    Mongodb Collection/Index 对应的数据文件
    MongoDB 查看索引被引用次数
    MongoDB 3.0新增的压缩选项(转载)
    Linux vmstat
    Mongodb按照日期分组统计
    MongoDB executionStats 详细分步查询计划与分步时间 explain("executionStats")(转载)
  • 原文地址:https://www.cnblogs.com/6262369sss/p/10785247.html
Copyright © 2011-2022 走看看