【BZOJ3601】一个人的数论(数论)
【BZOJ3601】一个人的数论(数论)
题面
BZOJ
怎么这图片这么大啊。。。
题解
要求的是\(\displaystyle \sum_{i=1}^n [gcd(i,n)=1]i^d\)
然后把\(gcd=1\)给拆了,\(\displaystyle \sum_{i=1}^n i^d\sum_{x|i,x|n}\mu(x)\)。
然后再把\(\mu\)丢掉前面去,\(\displaystyle \sum_{x|n}\mu(x)x^d\sum_{i=1}^{n/x}i^d\)
后面一半是自然数幂和,随便怎么搞都行。然而前面似乎就没法搞了QwQ。
首先自然数幂和可以写成一个多项式。
对于\(\sum_{i}i^d\)而言,一定可以写成一个\(d+1\)次多项式。
假装这个多项式的系数是\(a_i\)。
那么式子就可以改写成:\(\displaystyle \sum_{k=0}^d a_k\sum_{x|n}\mu(x)x^d[\frac{n}{x}]^k\)
后面这个东西很明显就是三个积性函数乘起来的,所以后面这个东西也是一个积性函数。
而现在已经给定了\(n\)的分解情况,那么只需要对于每个质数求解贡献。
而每个质因子的出现次数超过\(1\)的时候贡献就是\(0\),所以只有出现\(0\)次或者\(1\)次的时候有贡献,那么后面这个东西似乎就很好算的样子啦。
现在的问题就回到了如何求解\(a_i\)上面。
这个东西可以待定系数+高斯消元直接爆算出解。
也可以拉格朗日插值直接算。
#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 105
#define MOD 1000000007
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int ans,d,W,a[MAX],p[MAX],b[MAX],c[MAX],pro[MAX];
int fpow(int a,int b){int s=1;while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}return s;}
void pre()
{
for(int i=1;i<=d+2;++i)p[i]=(p[i-1]+fpow(i,d))%MOD;b[0]=1;
for(int i=0;i<=d+1;++i)
{
for(int j=i+1;j;--j)b[j]=(b[j-1]+MOD-1ll*b[j]*(i+1)%MOD)%MOD;
b[0]=1ll*b[0]*(MOD-i-1)%MOD;
}
for(int i=0;i<=d+1;++i)
{
int s=p[i+1],inv=fpow(i+1,MOD-2);
for(int j=0;j<=d+1;++j)if(i!=j)s=1ll*s*fpow((i-j+MOD)%MOD,MOD-2)%MOD;
b[0]=1ll*b[0]*(MOD-inv)%MOD;
for(int j=1;j<=d+2;++j)b[j]=(MOD-1ll*(b[j]+MOD-b[j-1])*inv%MOD)%MOD;
for(int j=0;j<=d+2;++j)a[j]=(a[j]+1ll*s*b[j])%MOD;
for(int j=d+2;j;--j)b[j]=(b[j-1]+MOD-1ll*b[j]*(i+1)%MOD)%MOD;
b[0]=1ll*b[0]*(MOD-i-1)%MOD;
}
}
int main()
{
d=read();W=read();pre();
for(int i=0;i<=d+1;++i)pro[i]=1;
while(W--)
{
int p=read(),v=read(),n=fpow(p,v),inv=fpow(p,MOD-2),pd=fpow(p,d);
for(int k=0;k<=d+1;++k)
{
int ret=(fpow(n,k)+MOD-1ll*pd*fpow(1ll*n*inv%MOD,k)%MOD)%MOD;
pro[k]=1ll*pro[k]*ret%MOD;
}
}
for(int i=0;i<=d+1;++i)ans=(ans+1ll*a[i]*pro[i])%MOD;
printf("%d\n",ans);
return 0;
}