逆元与素数

Posted xiaofengzai

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了逆元与素数相关的知识,希望对你有一定的参考价值。

A - Detachment

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5976

题解:这道题思路为:数学规律+逆元

这里有一个结论,对于一个数把他拆成从2开始的连续的数,最后得到的成绩是最大的,所以我们可以先预处理一下前缀和包括乘积后的结果。不过对于有些数无法用多个连续的数表示,比如对于11可以找到2+3+4,此时还余2,要构成5还差3,所以我们把3变成5即2 + 4 + 5.

之前是2+3+4

现在是2+4+5
但有种特殊情况,比如2+3+.. + l,此时还余个l,因此我们会为前l-1个数都加上1,还剩余一个1就加到最后一个数,

之前是2+3+4+...l,现在是3+4+5+6+...l+(l+2),对比两者的变化,相当于除去2,在乘上l+2,由于我们要进行取余,因此除法的操作就需要用到逆元(由费马小定理)

代码:

#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int N=50010;
ll sum[N],f[N];
void init()
{
    f[1]=1;
    for(int i=2;i<=N;i++)
    {
        sum[i]=sum[i-1]+i;
        f[i]=(f[i-1]*i)%mod;
    }
}

ll pow_s(ll a, ll n)
{
    ll res=1;
    while(n)
    {
        if(n&1)
            res=res%mod*a%mod;
        a=a%mod*a%mod;
        n>>=1;
    }
    return res;
}

int main()
{
    ll i,j,t,x;
    scanf("%lld",&t);
    init();
    while(t--)
    {
        scanf("%lld",&x);
        if(x<=4)
        {
            printf("%lld
",x);
            continue;
        }
        //二分法找到适合的l
        ll l=0,r=N;
        while(r-l>1)
        {
            ll mid=l+(r-l)/2;
            if(sum[mid]>x)
                r=mid;
            else
                l=mid;
        }
        ll leve=x-sum[l];
        ll res=f[l];
        if(leve==l)
            res=(res*pow_s(2,mod-2)%mod*(l+2))%mod;
        else
            res=(res*pow_s(l+1-leve,mod-2)%mod*(l+1))%mod;
        printf("%lld
",res);
    }
    return 0;
}

 

B - 寻找素数对

题目:http://acm.hdu.edu.cn/showproblem.php?pid=1262

题解:从这个数的一半的地方开始找,然后进行素数判断,循环即可实现

代码:

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
using namespace std;
int m;

bool check(int s)
{
    for(int i=2;i<=sqrt(s);i++)
    {
        if(s%i==0)
            return false;
    }
    return true;
}

int main()
{
    int i,j;
    while((scanf("%d",&m))!=EOF)
    {
        int k=m/2;
        for(i=k;i>0;i--)
        {
            int f=m-i;
            if(check(f)&&check(i))
            {
                cout<<i<<" "<<f<<endl;
                break;
            }
        }
    }
    return 0;
}

 

C - Max Factor

题目:http://acm.hdu.edu.cn/showproblem.php?pid=2710

题解:寻找最大的素数因子,我们可以利用埃式筛求素数的方法,打表找到所有数的最大的素数因子,然后进行比较选出最终的结果。

代码:

#include<iostream>
#include<cmath>
using namespace std;
const int MAXN=1e5;
int prime[MAXN+1];
int v[20010];
void aisoup()
{
    for(int i=0;i<=20010;i++)
        v[i]=1;
    for(int i=2;i<=20010;i++)
    {
        if(v[i]==1)
        {
            for(int j=i;j<=20010;j+=i)
                v[j]=i;
        }
    }
}

int main()
{
    int i,j,n,x,ans,maxn=-1;
    aisoup();
    while(cin>>n)
    {
        ans=0;
        maxn=-1;
        for(int i=0;i<n;i++)
        {
            cin>>x;
            if(v[x]>maxn)
            {
                maxn=v[x];
                ans=x;
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

 

D - Twin Prime Conjecture

题目:http://acm.hdu.edu.cn/showproblem.php?pid=3792

题解:题目的意思是找出小于等于他的相邻素数对相差为2的对数,一开始直接利用埃式筛求出素数,然后对于每个数进行一次计算,最终TLE了。

明显这样太过于浪费时间,存在重复计算,因此我们对其进行打表,算出ans数组,它对应的为相应数的素数对个数,而他们之间的关系为,i和i-1之间,比较二者对应的数与相差为2的数是否都为素数。

代码:

#include<iostream>
using namespace std;
const int N=1e5+10;
int prime[N];
bool v[N];
int ans[N];
void pre()
{
    int k=0;
    for(int i=2;i<N;i++)
    {
        if(!v[i])
        {
            prime[k++]=i;
            for(int j=2*i;j<N;j+=i)
            {
                v[j]=true;
            }
        }
    }
}
void f()
{
    pre();
    ans[1]=ans[2]=ans[3]=0;
    for(int i=4;i<N;i++)
    {
        if(!v[i]&&!v[i-2])
        {
            ans[i]=ans[i-1]+1;
        }
        else
            ans[i]=ans[i-1];
    }
}
int main()
{
    f();
    int n,i,j;
    while(cin>>n)
    {
        if(n<0)
            break;
        cout<<ans[n]<<endl;
    }
    return 0;
}

 

E - Squarefree number

题目:http://acm.hdu.edu.cn/showproblem.php?pid=3826

题解:这道题相对来说比较有难度,判断该数的因子当中是否有平方数。我们素数的筛选范围在106范围内,对于这个数的范围为1016次方。

输入一个n,如果可以被一个平方数整除,则不是平方自由数,输出no,否则输出yes
分析:显然要分解质因数,根据唯一分解定理分解。
n<=10^18,不能用暴力求到10^9的素数,欧拉筛一般只是找10^6内的素数。显然需要优化。
如果n>10^6,巨大,筛完了10^6内的质因子后,n还是大于10^6,则
1.如果n不是平方自由数,则因子中包含质因数的平方,则n=p*p,p是素数,p>10^6,除此之外没有别的大于10^6的因子了,否则n>10^18
2.如果n是平方自由数,则因子中不包含质因数的平方
(1)n是素数
(2)n不是素数,n=p1*p2,是两个大素数的乘积。p1,p2>10^6
 
代码:
#include<iostream>
#include<cmath>
#include<cstdio>
using namespace std;
typedef long long ll;
const int N=1e6+10;
ll t,n;
int prime[N];
bool v[N];
int k;
void pre()
{
    k=0;
    for(int i=2;i<N;i++)
    {
        if(!v[i])
        {
            prime[k++]=i;
            for(int j=i*2;j<N;j+=i)
                v[j]=true;
        }
    }
}
int main()
{
    ll i,j;
    cin>>t;
    pre();
    for(i=1;i<=t;i++)
    {
        cin>>n;
        bool flag=false;
        for(j=0;j<k;j++)
        {
            int sum=0;
            while(n%prime[j]==0)
            {
                n/=prime[j];
                sum++;
            }
            //sum>=2表示有平方数
            if(sum>=2)
            {
                flag=true;
                break;
            }
        }
        if(!flag&&n>1)
        {
            //判断是否为两个一样大的素数相乘
            ll s=sqrt(n);
            if(s*s==n)
                flag=true;
        }
        if(flag)
            printf("Case %d: No
",i);
        else
            printf("Case %d: Yes
",i);
    }
    return 0;
}

 

以上是关于逆元与素数的主要内容,如果未能解决你的问题,请参考以下文章

逆元与组合数

素数定理-欧几里得算法-乘法逆元

O(n)求素数,求欧拉函数,求莫比乌斯函数,求对mod的逆元,各种求

费马小定理(介绍+证明+逆元代码实现)

费马小定理(介绍+证明+逆元代码实现)

逆元总结