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 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 ( p q ) ,然后用线性筛得到所有 F F 值。根据定义显然有 F(pq)=G(pq)G(pq1) F ( p q ) = G ( p q ) − G ( p q − 1 ) 。然后对于每个询问下底函数分块,总时间为 O(n+Tn) O ( n + T n )

一开始 G G 数组乘起来的时候忘了取模,WA了一次。


CODE:

#include
#include
#include
#include
#include
#include
#include
#include
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; iif (!vis[i]) G[i]=Fast_power(i,k),p[i]=i,prime[++cur]=i;
        for (int j=1; j<=cur && i*prime[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; iif (p[i]for (int i=1; i1]+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;
}

你可能感兴趣的:(数论)