BZOJ4407:于神之怒加强版 (数论+线性筛)

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4407


题目分析:又是一道老年人数论题。

不妨令 nm n ≤ m 。经过一番推导,可以得到这个:

ans=D=1nnDmDd|Ddkμ(Dd) a n s = ∑ D = 1 n ⌊ n D ⌋ ⌊ m D ⌋ ∑ d | D d k μ ( D d )

G(i)=ik G ( i ) = i k ,它是个完全积性函数,可以通过预处理所有质数的 G G 然后线性筛得到。求质数的G值可以用快速幂。由于质数个数是 nln(n) n ln ⁡ ( n ) 级别的,所以这部分时间为 O(nlog(k)ln(n)) O ( n log ⁡ ( k ) ln ⁡ ( n ) ) 。又因为 n,k n , k 大小相近,可以认为接近 O(n) O ( n )

第二个 后面的式子记为 F(D) F ( D ) ,它是 G G μ,所以也是积性函数。令 p p 为质数,则可以预处理全部F(pq),然后用线性筛得到所有 F F 值。根据定义显然有F(pq)=G(pq)G(pq1)。然后对于每个询问下底函数分块,总时间为 O(n+Tn) O ( n + T n )

一开始 G G <script type="math/tex" id="MathJax-Element-2161">G</script>数组乘起来的时候忘了取模,WA了一次。


CODE:

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;

const int maxn=5000010;
const long long M=1000000007;
typedef long long LL;

int p[maxn];
LL G[maxn];
LL F[maxn];

bool vis[maxn];
int prime[maxn];
int cur=0;

int t,n,m,k;

LL Fast_power(LL x,LL y)
{
    if (!y) return 1LL;
    LL temp=Fast_power(x,y>>1);
    temp=temp*temp%M;
    if (y&1) temp=temp*x%M;
    return temp;
}

void Linear_shaker()
{
    G[1]=1;
    for (int i=2; i<maxn; i++)
    {
        if (!vis[i]) G[i]=Fast_power(i,k),p[i]=i,prime[++cur]=i;
        for (int j=1; j<=cur && i*prime[j]<maxn; j++)
        {
            int K=i*prime[j];
            vis[K]=true;
            G[K]=G[i]*G[ prime[j] ]%M;
            if (i%prime[j]) p[K]=prime[j];
            else
            {
                p[K]=p[i]*prime[j];
                break;
            } 
        }
    }

    F[1]=1LL;
    for (int i=1; i<=cur; i++)
    {
        LL x=prime[i],last=1;
        while (x<(long long)maxn)
        {
            F[x]=(G[x]-G[last]+M)%M;
            last=x;
            x*=(long long)prime[i];
        }
    }
    for (int i=2; i<maxn; i++)
        if (p[i]<i) F[i]=F[ p[i] ]*F[ i/p[i] ]%M;
    for (int i=1; i<maxn; i++) F[i]=(F[i-1]+F[i])%M;
}

int main()
{
    freopen("4407.in","r",stdin);
    freopen("4407.out","w",stdout);

    scanf("%d%d",&t,&k);
    Linear_shaker();
    while (t--)
    {
        scanf("%d%d",&n,&m);
        if (n>m) swap(n,m);
        LL ans=0;
        int last;
        for (int i=1; i<=n; i=last+1)
        {
            last=min( n/(n/i) , m/(m/i) );
            ans=(ans+ (long long)(n/i)*(m/i)%M*( F[last]-F[i-1]+M )%M )%M;
        }
        printf("%I64d\n",ans);
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值