BZOJ 3601

10 篇文章 0 订阅
6 篇文章 0 订阅

BZOJ 3601

文章来自我的新博客

Description:

        给定一个非负整数 d,(d<=100) 和正整数 N ,令 fd(N) 为所有小于 N 且与 N 互质的数的 d 次方和。 对于给定的 d,N fd(N) Mod 109+7 的值。
        由于 N 很大,所以给出 N 的质因数分解式。

N=i=1wpaii(2<=pi<=109 , 1<=ai<=109)

       
       
       

Solution:

        莫比乌斯反演之后有: fd(N)=k|Nμ(k)Nki=1(ik)d=k|Nμ(k)kdNki=1id

        显然,可以构造数组 {a} 使得 Ti=1id=d+1i=0aiTd

        那么假设我们构造出了数组 {a} ,那么 fd(N)=k|Nμ(k)kdd+1i=0ai(Nk)i=k|Nμ(k)d+1i=0aiNikdi=d+1i=0aiNik|Nμ(k)kdi

        μ(k)kdi 显然这是个积性函数, 所以 k|Nμ(k)kdi=wi=1(1+μ(pi)pdii)=wi=1(1pdii)

        所以 fd(N)=d+1i=0aiNiwi=1(1pdii) ,这样可以轻松做到 O(dw) 的时间复杂度。但是 {a} 怎么求呢,这很简单,高斯消元即可。所以总时间复杂度 O(dw+d3)
       
       
       

Code:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>

using namespace std;

const long long Mod=1e9+7;

long long fc[110][110]={{0}};
long long a[110]={0};
int d,W;
long long p_N[110]={0};
int D[1010]={0};
long long temp[1010]={0};

long long power(long long a,long long k)
{
    long long o=1;
    if(k<0) k+=Mod-1;
    for(;k>0;k>>=1)
    {
        if(k&1) o=o*a%Mod;
        a=a*a%Mod;
    }
    return o;
}

void Gauss()
{
    for(int i=1;i<=d+2;i++)
    {
        int g=i;
        for(int j=i;j<=d+2;j++)
            if(fc[j][i]!=0)
            {
                g=j;
                break;
            }
        if(g!=i) swap(fc[i],fc[g]);
        for(int j=1;j<=d+2;j++)
            if(j!=i && fc[j][i]!=0)
            {
                long long tmp=fc[j][i]*power(fc[i][i],Mod-2)%Mod;
                for(int p=0;p<=d+2;p++)
                    fc[j][p]=(fc[j][p]-tmp*fc[i][p]%Mod+Mod)%Mod;
            }
    }
    for(int i=1;i<=d+2;i++)
        a[i-1]=fc[i][0]*power(fc[i][i],Mod-2)%Mod;
    return;
}

long long N=1;

int main()
{
    scanf("%d%d",&d,&W);
    long long sum=0;
    for(int i=1;i<=d+2;i++)
    {
        sum=(sum+power(i,d))%Mod;
        fc[i][0]=sum;
        for(int j=1;j<=d+2;j++)
            fc[i][j]=power(i,j-1);
    }
    Gauss();
    for(int i=1;i<=W;i++)
    {
        int p,q;
        scanf("%d%d",&p,&q);
        N=N*power(p,q)%Mod;
        D[i]=p;
        temp[i]=power(p,Mod-2);
    }
    p_N[0]=1;
    for(int i=1;i<=d+1;i++)
        p_N[i]=p_N[i-1]*N%Mod;
    long long ans=0;
    for(int i=d+1;i>=0;i--)
    {
        long long cnt=1;
        for(int j=1;j<=W;j++)
        {
            cnt=cnt*(1-temp[j]+Mod)%Mod;
            temp[j]=temp[j]*D[j]%Mod;
        }
        ans=(ans+a[i]*p_N[i]%Mod*cnt)%Mod;
    }
    cout<<ans<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值