文章目录
- 前言
- 回忆
- 题集
- 1
- 2
- 杜教筛
- 例题
前言
这里需要对莫反有一些基础。
不会的可以点这里
回忆
- f ( n ) = ∑ d ∣ n g ( d ) → g ( n ) = ∑ d ∣ n f ( d ) μ ( n d ) f(n)=\sum_{d|n}g(d)\rightarrow g(n)=\sum_{d|n}f(d)\mu(\frac{n}{d}) f(n)=∑d∣ng(d)→g(n)=∑d∣nf(d)μ(dn)
- ∑ d ∣ n μ ( d ) = [ n = 1 ] \sum_{d|n}\mu(d)=[n=1] ∑d∣nμ(d)=[n=1]
- ∑ i = 1 n ⌊ n i ⌋ = \sum_{i=1}^n\left\lfloor\frac{n}{i}\right\rfloor= ∑i=1n⌊in⌋= 你应该知道怎么求
- ∑ i = 1 1 0 9 μ ( i ) = \sum_{i=1}^{10^9}\mu(i)= ∑i=1109μ(i)= 你可能需要知道怎么求
- 线性筛 μ ( i ) , φ ( i ) \mu(i),\varphi(i) μ(i),φ(i)
- 一些数学能力
题集
1
∏
i
=
1
n
∏
j
=
1
m
gcd
(
i
,
j
)
\large\prod_{i=1}^n\prod_{j=1}^m\gcd(i,j)
∏i=1n∏j=1mgcd(i,j)
=
∏
d
=
1
d
∑
i
=
1
∑
j
=
1
m
[
gcd
(
i
,
j
)
=
d
]
=\prod_{d=1}d^{\sum_{i=1}\sum_{j=1}^m[\gcd(i,j)=d]}
=∏d=1d∑i=1∑j=1m[gcd(i,j)=d]
=
∏
d
=
1
d
∑
k
=
1
min
(
n
,
m
)
d
μ
(
k
)
n
k
d
m
k
d
=\prod_{d=1}d^{\sum_{k=1}^\frac{\min(n,m)}{d}\mu(k)\frac{n}{kd}\frac{m}{kd}}
=∏d=1d∑k=1dmin(n,m)μ(k)kdnkdm
=
∏
T
=
1
(
∏
k
∣
T
(
T
k
)
μ
(
k
)
)
n
T
m
T
=\prod_{T=1}(\prod_{k|T}(\frac{T}{k})^{\mu(k)})^{\frac{n}{T}\frac{m}{T}}
=∏T=1(∏k∣T(kT)μ(k))TnTm
令
f
(
T
)
=
∏
k
∣
T
(
T
k
)
μ
(
k
)
f(T)=\prod_{k|T}(\frac{T}{k})^{\mu(k)}
f(T)=∏k∣T(kT)μ(k)
线性筛+整出分块即可
Code:
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
#define cou(i) cout<<fixed<<setprecision(i)
using namespace std;
const int N=1e7+1,mod=1e9+7;
int t,n,m,k,ans,res;
unordered_map<int,int>Mu;
struct fy{
int prv[N],cnt,mu[N],F[N];
bool pr[N];
int qmi(int x,int y){
int res=1;
while(y>0){
if(y&1)
res=res*x%mod;
x=x*x%mod,y>>=1;
}
return res;
}
void ola(int x){
pr[1]=mu[1]=F[1]=1;
for(int i=2;i<=x;i++){
if(!pr[i])
prv[++cnt]=i,mu[i]=-1,F[i]=i;
for(int j=1;j<=cnt&&i*prv[j]<=x;j++){
int u=i*prv[j];
pr[u]=1;
if(i%prv[j]==0){
mu[u]=0;
F[u]=F[i];
break;
}
else{
mu[u]=-mu[i];
F[u]=1;
}
}
}
}
void getsum(int x){
F[0]=1;
for(int i=1;i<=x;i++){
F[i]*=F[i-1],F[i]%=mod;
}
}
int summu(int x){
int res=1;
if(x<N)
return mu[x];
if(Mu[x])
return Mu[x];
for(int l=2,r;l<=x;l=r+1){
r=x/(x/l);
res-=(summu(x/l))*(r-l+1);
}
Mu[x]=res;
return res;
}
int sumphi(int x){
int res=0;
for(int l=1,r;l<=x;l=r+1){
r=x/(x/l);
res+=(summu(r)-summu(l-1))*(x/l)*(x/l);
}
return res;
}
}A;
signed main(){
IOS;
A.ola(N-1);
A.getsum(N-1);
cin>>t;
while(t--){
cin>>n>>m;
int ans=1ll;
for(int l=1,r;l<=min(n,m);l=r+1){
r=min(n/(n/l),m/(m/l));
int res=A.F[r]*A.qmi(A.F[l-1],mod-2)%mod;
ans=ans*A.qmi(res,(n/l)*(m/l))%mod;
}
cout<<ans<<"\n";
}
return 0;
}
经验:
1
2
3
2
Link
暴力推式子。
关键:
gcd
(
i
j
,
j
k
,
k
i
)
=
gcd
(
i
,
j
)
gcd
(
j
,
k
)
gcd
(
k
,
i
)
gcd
(
i
,
j
,
k
)
\large{\gcd(ij,jk,ki)=\frac{\gcd(i,j)\gcd(j,k)\gcd(k,i)}{\gcd(i,j,k)}}
gcd(ij,jk,ki)=gcd(i,j,k)gcd(i,j)gcd(j,k)gcd(k,i)
然后可得原式=于神之怒加强版或这里
好像这黑题有那么一点点水啊
Code:
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
using namespace std;
const int N=2e7+1,mod=1e9+7;
int t,n,m,p,k;
struct fy{
int prv[N],cnt,g[N],s[N];
bool pr[N];
inline int qmi(int x,int y){
int res=1;
while(y>0){
if(y&1)
res=res*x%mod;
x=x*x%mod,y>>=1;
}
return res;
}
void ola(int x){
pr[1]=g[1]=1;
for(int i=2;i<=x;i++){
if(!pr[i])
prv[++cnt]=i,g[i]=(qmi(i,k)-1+mod)%mod;
for(int j=1;j<=cnt&&i*prv[j]<=x;j++){
int u=i*prv[j];
pr[u]=1;
if(i%prv[j]==0){
g[u]=g[i]*qmi(prv[j],k)%mod;
break;
}
else{
g[u]=g[i]*g[prv[j]]%mod;
}
}
}
}
void getsum(int x){
for(int i=1;i<=x;i++)
g[i]=(g[i]+g[i-1])%mod;
}
}A;
signed main(){
IOS;
k=2;
cin>>t;
A.ola(N-1);
A.getsum(N-1);
while(t--){
cin>>n>>m>>p;
int ans=0,res=0;
for(int l=1,r;l<=min(n,m);l=r+1){
r=min(n/(n/l),m/(m/l));
res+=(n/l)*(m/l)%mod*((A.g[r]-A.g[l-1]+mod)%mod)%mod;
res%=mod;
}
ans+=res*p;
ans%=mod;
res=0;
for(int l=1,r;l<=min(m,p);l=r+1){
r=min(m/(m/l),p/(p/l));
res+=(m/l)*(p/l)%mod*((A.g[r]-A.g[l-1]+mod)%mod)%mod;
res%=mod;
}
ans+=res*n;
ans%=mod;
res=0;
for(int l=1,r;l<=min(p,n);l=r+1){
r=min(p/(p/l),n/(n/l));
res+=(p/l)*(n/l)%mod*((A.g[r]-A.g[l-1]+mod)%mod)%mod;
res%=mod;
}
ans+=res*m;
ans%=mod;
cout<<ans<<"\n";
}
return 0;
}
杜教筛
来自 OI-wiki的资料:
这里直达
这里直达OI-wiki
例题
给定一个正整数,求
a
n
s
1
=
∑
i
=
1
n
φ
(
i
)
,
a
n
s
2
=
∑
i
=
1
n
μ
(
i
)
ans_1=\sum_{i=1}^n\varphi(i),ans_2=\sum_{i=1}^n \mu(i)
ans1=∑i=1nφ(i),ans2=∑i=1nμ(i)
输入的第一行为一个整数,表示数据组数
T
T
T。
接下来
T
T
T 行,每行一个整数
n
n
n,表示一组询问。
对于每组询问,输出一行两个整数,分别代表
a
n
s
1
ans_1
ans1 和
a
n
s
2
ans_2
ans2。
对于全部的测试点,保证
1
≤
T
≤
10
1 \leq T \leq 10
1≤T≤10,
1
≤
n
<
2
31
1 \leq n \lt 2^{31}
1≤n<231。
考虑使用杜教筛。
设
S
1
(
n
)
=
∑
i
=
1
n
μ
(
i
)
S_1(n)=\sum_{i=1}^n\mu(i)
S1(n)=∑i=1nμ(i)
那样我们就完成了两个最简单的例题了
Code:
#include<bits/stdc++.h>
#define int __int128
using namespace std;
const int N=2e6+1,mod=1e9+7;
int t,n,m,k;
void write(int x){
if(x<0)
putchar('-'),x=-x;
if(x>=10)
write((int)(x/10));
char o='0'+x%10;
putchar(o);
}
void read(int &x){
x=0;
int y=1;
char c=getchar();
while(c>'9'||c<'0'){
if(c=='-'){
y=-1;
break;
}
c=getchar();
}
while(c<='9'&&c>='0')
x=x*10+c-'0',c=getchar();
x*=y;
}
map<int,int>Mu,Phi;
struct fy{
int prv[N],cnt,phi[N],mu[N];
bool pr[N];
void ola(int x){
pr[1]=mu[1]=1;
for(int i=2;i<=x;i++){
if(!pr[i])
prv[++cnt]=i,mu[i]=-1;
for(int j=1;j<=cnt&&i*prv[j]<=x;j++){
int u=i*prv[j];
pr[u]=1;
if(i%prv[j]==0){
mu[u]=0;
break;
}
else{
mu[u]=-mu[i];
}
}
}
}
void getsumF(int x){
for(int i=1;i<=x;i++)
mu[i]+=mu[i-1];
}
int summu(int x){
int res=1;
if(x<N)
return mu[x];
if(Mu[x])
return Mu[x];
for(int l=2,r;l<=x;l=r+1){
r=x/(x/l);
res-=(summu(x/l))*(r-l+1);
}
Mu[x]=res;
return res;
}
int sumphi(int x){
int res=0;
for(int l=1,r;l<=x;l=r+1){
r=x/(x/l);
res+=(summu(r)-summu(l-1))*(n/l)*(n/l);
}
return res;
}
}A;
signed main(){
A.ola(N-1);
A.getsumF(N-1);
read(t);
while(t--){
read(n);
write((A.sumphi(n)-1)/2+1);
putchar(' ');
write(A.summu(n));
putchar('\n');
}
return 0;
}
//此代码有一点瑕疵,但确实可以过模板题