JZOJ 4161. 于神之怒

Description

Description

Input

一行三个数,m, n, k。

Output

Output

Sample Input

样例1

3 3 2

样例2

5000000 5000000 4000000

Sample Output

样例1

20

样例2

​913111630

Data Constraint

Data Constraint

Solution

  • 直接上莫比乌斯反演:

Ans=i=1nj=1mgcd(i,j)k A n s = ∑ i = 1 n ∑ j = 1 m g c d ( i , j ) k

Ans=ddki=1nj=1m[gcd(i,j)=d] A n s = ∑ d d k ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = d ]

Ans=ddki=1ndj=1md[gcd(i,j)=1] A n s = ∑ d d k ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ [ g c d ( i , j ) = 1 ]

Ans=ddki=1ndj=1mdu|gcd(i,j)μ(u) A n s = ∑ d d k ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ ∑ u | g c d ( i , j ) μ ( u )

  • 这一步是因为:

    d|nμ(d)=[n=1] ∑ d | n μ ( d ) = [ n = 1 ]

  • 于是我们继续变换:

    Ans=ddkuμ(u)ndumdu A n s = ∑ d d k ∑ u μ ( u ) ⌊ n d u ⌋ ⌊ m d u ⌋

  • D=du D = d u ,再将 D D 提到前面(这是为了凑形式):

    Ans=DnDmDd|Ddk·μ(Dd)

  • 令单位幂函数 idk(d)=dk i d k ( d ) = d k ,那么有:

    Ans=DnDmDf(D) A n s = ∑ D ⌊ n D ⌋ ⌊ m D ⌋ f ( D )

  • 其中:

    f(D)=d|Didk(d)μ(Dd) f ( D ) = ∑ d | D i d k ( d ) · μ ( D d )

  • 是狄利克雷卷积!即: f=idkμ f = i d k ∗ μ

  • 对于 f f 这个积性函数,我们可以用线筛求出(跟筛 φ 差不多)。

  • 之后做出前缀和就可以分块求答案了。

  • 这种算法明显优于其它什么两次分块的算法,可以做多组询问。

  • 就是提前给出 k k ,每次询问给出一组 n,m ,范围还是 n,m,k5106 n , m , k ≤ 5 ∗ 10 6 ,询问数 T2000 T ≤ 2000

  • 那先预处理出 f f ,每次 O(n) 回答询问,时间复杂度 O(n+Tn) O ( n + T n )

Code

#include<cstdio>
#include<algorithm>
#include<cctype>
using namespace std;
typedef long long LL;
const int N=5e6+5,mo=1e9+7;
int f[N],g[N];
bool bz[N];
inline int read()
{
    int X=0,w=0; char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
inline int ksm(int x,int y)
{
    int s=1;
    while(y)
    {
        if(y&1) s=(LL)s*x%mo;
        x=(LL)x*x%mo;
        y>>=1;
    }
    return s;
}
inline int min(int x,int y)
{
    return x<y?x:y;
}
int main()
{
    int n=read(),m=read(),k=read(),ans=0;
    g[1]=1;
    for(int i=2;i<N;i++)
    {
        if(!bz[i])
        {
            f[++f[0]]=i;
            g[i]=ksm(i,k)-1;
        }
        for(int j=1;j<=f[0] && i*f[j]<N;j++)
        {
            bz[i*f[j]]=true;
            if(i%f[j]==0)
            {
                g[i*f[j]]=(LL)g[i]*(g[f[j]]+1)%mo;
                break;
            }
            g[i*f[j]]=(LL)g[i]*g[f[j]]%mo;
        }
    }
    for(int i=1;i<N;i++) g[i]=(g[i-1]+g[i])%mo;
    if(n>m) swap(n,m);
    for(int i=1,p;i<=n;i=p+1)
    {
        p=min(n/(n/i),m/(m/i));
        ans=(ans+(LL)(n/p)*(m/p)%mo*(g[p]-g[i-1]+mo)%mo)%mo;
    }
    printf("%d\n",ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值