(one day one problem)SPOJ - PROD1GCD Product it again (GCD+分解质因子找思路)-数论

题目链接:https://cn.vjudge.net/problem/477808/origin

The problem is very simple. given two integers n and m, find the product GCD(1, 1) * GCD(1, 2) * ... * GCD(1, M) * GCD(2, 1) * GCD(2, 2) * ... * GCD(2, M) * ... * GCD(N, 1) * GCD(N, 2) * ... * GCD(N, M).

Input

The first line will be the number of test cases t, followed by t lines , each having two numbers n and m (1 <= n, m <= 10000000) (1 <= t <= 5).

Output

Output the required solution modulo 10^9+7.

Example

Input:
1
5 6

Output:
5760
题目大意:

给定t组数据,每组两个整数n和M,求GCD(1,1)*GCD(1,2)*.....*GCD(1,M)*GCD(2,1)*GCD(2,2)*...*GCD(2,M)*...*GCD(N,1)*GCD(N,2)*...*GCD(N,M)对10^9+7取余的结果

题解:

当看到这个题的时候有点没有思路,当然还是知道该用分解定理的,但是找不出具体的解决方法。最后在讨论中才解决的。既然是GCD那就是两个数中共有的部分(这里指分解了的质因子)。他们的质因子都是素数,那就枚举每个素数的乘积就行了。比如枚举素数p,那就找在(n,m)内的所有对的GCD中包含素数p的个数。那么怎么找p出现的个数呢。可以想:在1-n内的数能够分解后包含p的数肯定能整出p,也就是p的倍数,那么就是n/p就是p的倍数的个数,同理再找一下m的,m/p就是p的倍数的个数。那么(n/p)*(m/p)就是在所有的对中能过分解出来p的个数。也就是说这些都包含p.但是有的GCD(x,y)也可能包含多个p,那就继续找p^k的个数相乘,直到p^k>min(n,m)为止。

这样知道每个素数出现的个数后乘起来就可以了,为了防止超时用快速幂求。如果不明白的话可以找个特例在纸上求证一下它的过程,首先把所有的GCD(x,y)乘起来的过程写下来。

#include <iostream>
#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
const LL mod=1000000007;
LL pri[10000007];
bool vis[10000007];
int ant=0;
///获取素数(素数筛)
void prime()
{
    memset(vis,false,sizeof(vis));
    int n=10000007;
    for(int i=2;i<=sqrt(n+0.5);i++)
    {
        if(!vis[i])
        {
            for(int j=i*i;j<=n;j+=i)vis[j]=true;
        }
    }
    for(int i=2;i<=n;i++)
    {
        if(!vis[i])pri[ant++]=i;
    }
}
///获取素数p在(1-n)*(1-m)中的个数
LL getcnt(LL p,LL n,LL m)
{
    LL k=p;
    LL cnt=0;
    while(k<=min(n,m))
    {
        ///既然是gcd那么就是n和m共有的质因子,所以相乘
        cnt+=(n/k)*(m/k);///n/k表示1~n内是k的倍数的个数(是k的倍数肯定能分解输出
        ///k来,同理m一样)
        k=k*p;///但是对于1~n内的数i不一定只能分解一个素数p,可能是多个
        ///所以要p的多方
    }
    return cnt;
}
LL pow1(LL a,LL n)///幂取模
{
    if(n==0)return 1;
    LL x=pow1(a,n/2);
    LL ans=x*x%mod;
    if(n%2==1)ans=ans*a%mod;
    return ans;
}
int main()
{
    int t;
    prime();///获取素数
    scanf("%d",&t);
    while(t--)
    {
        LL n,m;
        scanf("%lld%lld",&n,&m);
        LL ans=1;///总乘积
        for(int i=0;i<ant&&pri[i]<=min(n,m);i++)
        {
            ans*=pow1(pri[i],getcnt(pri[i],n,m));
            ans=ans%mod;
        }
        printf("%lld\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值