2020牛客暑期多校训练营第四场Basic Gcd Problem(数论,快速幂,素数筛,记忆化)

题目

传送门

题目大意

已知函数:
在这里插入图片描述
给定一些正整数对(ni, ci),输出
在这里插入图片描述

分析

不难发现当x>1时,fc(x)=c*fc(x的最大因数1),于是便先用类似素数筛的玩意儿筛出每个数的最大因数,如下:

for(int i=2;i<=MAXN;i++)
{
	if(!dv[i])//dv[i]存储i的最大因子,若为0则说明是该数为质数从未被筛到
	{
		for(int j=i;j<=MAXN;j+=i) dv[j]=max(dv[j],j/i);//对其每个倍数进行标记,可以保证最后一次被更改时存储的是最大因数
	}
}

当然还有一种更高效的:就采用原汁原味的素数筛存储每个数的最小质因数,不难得出:对于一个非质数n(n≠1且n≠0),n=n的最小质因数*n的最大因数,所以可以用n/n的最大质因数来得到最大因数。这样可以保证每个数只被筛到一次,而不会像上面那样会被n的每个质因数筛到,节省时间,这里就不再叙述了。
有了dv数组之后便可以在dp数组中存储每个不乘c的f(x),接着考虑c这一部分。
我们发现fc(x)乘的c的个数就是递归层数,于是我们可以用记忆化搜索存下每个数的递归层数,对于不同的c进行快速幂再与之前的dp相乘即可,轻松解决。

代码

#include 
#define ll long long
using namespace std;
 
const ll mod=1e9+7;
const int MAXN=1010101;
bool isprime[MAXN];
ll T,n,c,num=1,dp[MAXN],sum[MAXN],lis[MAXN];
int dv[MAXN];
 
ll gcd(ll x,ll y){return y==0?x:gcd(y,x%y);}
 
ll ksm(ll x,ll y)
{
    ll ret=1;
    while(y)
    {
        if(y&1) ret=ret*x%mod;
        x=x*x%mod;
        y>>=1;
    }
    return ret;
}
 
int main()
{
    for(int i=2;i<=MAXN;i++) if(!dv[i]) for(int j=i;j<=MAXN;j+=i) dv[j]=max(dv[j],j/i);
    dp[1]=1,sum[1]=0;
    for(scanf("%lld",&T);T--;)
    {
        scanf("%lld%lld",&n,&c);
        if(n<=num) printf("%lld\n",dp[n]*ksm(c,sum[n]));
        else
        {
            for(int i=num+1;i<=n;i++) dp[i]=dp[dv[i]],sum[i]=sum[dv[i]]+1;
            num=n;
            printf("%lld\n",dp[n]*ksm(c,sum[n]));
        }
    }
}

  1. 本文讨论一个数的最大因数时不考虑该数本身 ↩︎

你可能感兴趣的:(2020牛客暑期多校训练营第四场Basic Gcd Problem(数论,快速幂,素数筛,记忆化))