隐藏
「bzoj3601」一个人的数论 - 莫比乌斯反演+自然数幂和 | Bill Yang's Blog
0%

「bzoj3601」一个人的数论 - 莫比乌斯反演+自然数幂和

题目大意

    定义$f_d(n)=\sum\limits_{i=1}^ni^d[\gcd(i,n)=1]$


题目分析

这是一个很经典的问题,这里有一份写的很好的题解。
首先考虑对原式进行莫比乌斯反演。

令$h(n)=\sum\limits_{i=1}^ni^d$,我们称$h$为自然数幂和。

自然数幂和有很多种解法,并且其可以表示为多项式形式。

注意自然数幂和无常数项
本题可以用是$O(d^3)$的高斯消元,$O(d^2)$的拉格朗日插值法,$O(d\log d)$的伯努利数,$O(1)$的打表求出$a_i$,这样就可以将自然数幂和用多项式表示出来。
在时空间允许的范围内,一般还是选择打表和高斯消元,其余两个太复杂了。
因此,我们可以得到$a_i$的系数,原式变为:

令:

原式变为:

令$r(k)=\mu(k)k^d$,则:

这是两个积性函数的狄利克雷卷积,因此$g_i(n)$是积性函数。
但因为$g_i(n)$不是完全积性函数,故对$n$作质因数分解。

因此我们只需要考虑求出$g_i(p_j^{q_j})$即可。

对$\mu(p^j)p^{jd}(\frac{p^q}{p^j})^i$讨论:

  • 当$j=0$时,$\mu(p^j)=\mu(1)=1$,该式为$p^{qi}$
  • 当$j=1$时,$\mu(p^j)=\mu(p)=-1$,该式为$-p^{qi+d-i}$
  • 否则,$\mu(p^j)=0$,该式为$0$

因此,$g_i(p^q)=p^{qi}-p^{qi+d-i}$,可以$O(w\log q_i)$求出。

总复杂度为$O(d^3+dw\log q_i)$或$O(dw\log q_i)$。


代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<climits>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;

typedef long long LL;

inline const LL Get_Int() {
LL num=0,bj=1;
char x=getchar();
while(x<'0'||x>'9') {
if(x=='-')bj=-1;
x=getchar();
}
while(x>='0'&&x<='9') {
num=num*10+x-'0';
x=getchar();
}
return num*bj;
}

const int maxd=105,maxw=1005;
const LL mod=1e9+7;

LL d,w,a[maxd][maxd],ans[maxd],p[maxw],up[maxw];

LL Quick_Pow(LL a,LL b) {
LL sum=1;
for(; b; b>>=1,a=a*a%mod)if(b&1)sum=sum*a%mod;
return sum;
}

LL inv(LL x) {
return Quick_Pow(x,mod-2);
}

void Simplify(int n,int x) {
if(!a[x][x]) { //选主元
for(int i=x+1; i<=n; i++)
if(a[i][x]) {
for(int j=1; j<=n+1; j++)swap(a[i][j],a[x][j]);
break;
}
}
LL tmp=inv(a[x][x]);
for(int i=x+1; i<=n; i++) {
if(!a[i][x])continue;
LL mul=a[i][x]*tmp%mod;
for(int j=x; j<=n+1; j++)a[i][j]=(a[i][j]-a[x][j]*mul%mod+mod)%mod;
}
}

void Gauss(int n) {
for(int i=1; i<=n; i++)Simplify(n,i);
for(int i=n; i>=1; i--) { //回代
for(int j=i+1; j<=n; j++)a[i][n+1]=(a[i][n+1]-a[i][j]*ans[j]%mod+mod)%mod;
ans[i]=a[i][n+1]*inv(a[i][i])%mod;
}
}

int main() {
d=Get_Int();
w=Get_Int();
for(int i=1; i<=w; i++) {
p[i]=Get_Int();
up[i]=Get_Int();
}
LL sum=0;
for(int i=1; i<=d+1; i++) { //构造方程
a[i][1]=i;
for(int j=2; j<=d+1; j++)a[i][j]=a[i][j-1]*i%mod;
a[i][d+2]=(a[i-1][d+2]+Quick_Pow(i,d))%mod;
}
Gauss(d+1);
LL Ans=0;
for(int i=1; i<=d+1; i++) {
LL sum=1;
for(int j=1; j<=w; j++) {
LL tmp=(Quick_Pow(p[j],up[j]*i)-Quick_Pow(p[j],up[j]*i+d-i)+mod)%mod;
sum=sum*tmp%mod;
}
Ans=(Ans+ans[i]*sum%mod)%mod;
}
printf("%lld\n",Ans);
return 0;
}
姥爷们赏瓶冰阔落吧~