莫比乌斯反演
数论分块
常与数列分块连用
向下取整括号一定要加对
int End=0,N=a/d,M=b/d;
if(N<M) swap(N,M);
for(int Start=1;Start<=M;Start=End+1)
{
End=min(N/(N/Start),M/(M/Start));//注意边界
ans+=(sum[End]-sum[Start-1])*(long long)(N/Start)*(M/Start);//注意括号
}
常见公式
筛法求约数个数
OIWIKI
设\(f_i\)表示\(i\)的约数个数,\(g_i\)表示\(i\)的最小质因子的\(p^0+p^1+p^2+...+p^k\)
点击查看代码
vector<int> pri;
bool not_prime[N];
int g[N], f[N];
void pre(int n) {
g[1] = f[1] = 1;
for (int i = 2; i <= n; ++i) {
if (!not_prime[i]) {
pri.push_back(i);
g[i] = i + 1;
f[i] = i + 1;
}
for (int pri_j : pri) {
if (i * pri_j > n) break;
not_prime[i * pri_j] = true;
if (i % pri_j == 0) {
g[i * pri_j] = g[i] * pri_j + 1;
f[i * pri_j] = f[i] / g[i] * g[i * pri_j];
break;
}
f[i * pri_j] = f[i] * f[pri_j];
g[i * pri_j] = 1 + pri_j;
}
}
}
例题
P3455 [POI2007] ZAP-Queries
最基础的一题
假设\(a<=b\)
单次询问\(O(\sqrt n)\)
点击查看代码
#include <bits/stdc++.h>
#define speed() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ll long long
#define ull unsigned long long
#define lid (rt<<1)
#define rid (rt<<1|1)
// #define endl '\n'
//#define int long long
#define pb push_back
// #pragma comment(linker, “/STACK:512000000,512000000”)
using namespace std;
const int N = 1e6+5;
ll mu[N],n,tot,pr[N],m,sum[N];bool is[N];
ll a,b,d;
void getMu(int n)
{
mu[1]=1;
for(int i=2;i<=n;i++)
{
if(!is[i])
{
pr[++tot]=i;
mu[i]=-1;
}
for(int j=1;j<=tot&&i*pr[j]<=n;j++)
{
is[i*pr[j]]=1;
if(i%pr[j]==0)
{
mu[i*pr[j]]=0;
break;
}
mu[i*pr[j]]=-mu[i];
}
}
for(int i=1;i<=n;i++)
{
// cout<<mu[i]<<" ";
sum[i]=sum[i-1]+mu[i];
}
}
int main()
{
speed();
// freopen("in.in","r",stdin);
// freopen("out.out","w",stdout);
int T;
cin>>T;
getMu(5e4);
while(T--)
{
cin>>a>>b>>d;
ll ans=0;
if(a>b)swap(a,b);
// int End=0,N=a/d,M=b/d;
// if(N<M) swap(N,M);
// for(int Start=1;Start<=M;Start=End+1)
// {
// End=min(N/(N/Start),M/(M/Start));
// ans+=(sum[End]-sum[Start-1])*(long long)(N/Start)*(M/Start);
// }
int l=1,r;a/=d;b/=d;
while(l<=a)
{
r=min(a/(a/l),b/(b/l));
ans+=1ll*(sum[r]-sum[l-1])*(long long)(a/l)*(b/l);
l=r+1;
}
cout<<ans<<endl;
}
return 0;
}
P2522 [HAOI2011] Problem b
与上一题相似,只需要容斥一下即可,具体为\(ans((1,b),(1,d))-ans((1,a-1),(1,d))-ans((1,b),(1,c-1))+ans((a-1),(c-1))\)
点击查看代码
#include <bits/stdc++.h>
#define speed() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ll long long
#define ull unsigned long long
#define lid (rt<<1)
#define rid (rt<<1|1)
// #define endl '\n'
//#define int long long
#define pb push_back
#define pii pair<int,int>
using namespace std;
const int N =5e4+5;
int a,b,c,d,k,mu[N],tot,pr[N],sum[N];bool is[N];
void getMu(int n)
{
mu[1]=1;
for(int i=2;i<=n;i++)
{
if(!is[i])
{
pr[++tot]=i;
mu[i]=-1;
}
for(int j=1;j<=tot&&pr[j]*i<=n;j++)
{
is[pr[j]*i]=1;
if(i%pr[j]==0)break;
mu[i*pr[j]]=-mu[i];
}
}
for(int i=1;i<=n;i++)
{
sum[i]=sum[i-1]+mu[i];
// cout<<mu[i]<<" ";
}
}
ll solve(ll n,ll m,ll k)
{
n/=k;m/=k;
if(n>m)swap(n,m);
int l=1,r;
ll res=0;
while(l<=n)
{
r=min(n/(n/l),m/(m/l));
res+=(sum[r]-sum[l-1])*(n/l)*(m/l);
l=r+1;
}
// cout<<res<<endl;
return res;
}
int main()
{
speed();
// freopen("in.in","r",stdin);
// freopen("out.out","w",stdout);
int T;
cin>>T;
// cout<<"*****"<<endl;
getMu(5e4);
// cout<<"*****"<<endl;
while(T--)
{
cin>>a>>b>>c>>d>>k;
cout<<solve(b,d,k)-solve(a-1,d,k)-solve(b,c-1,k)+solve(a-1,c-1,k)<<endl;
}
return 0;
}
P2257 YY的GCD
同理
这样还没完,会超时,我们设\(T=kt\),则
我们发现这东西是可以预处理出来的(类似狄利克雷前缀和)
点击查看代码
#include <bits/stdc++.h>
#define speed() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ll long long
#define ull unsigned long long
#define lid (rt<<1)
#define rid (rt<<1|1)
// #define endl '\n'
//#define int long long
#define pb push_back
// #pragma comment(linker, “/STACK:512000000,512000000”)
using namespace std;
const int N = 1e7+5;
int mu[N],n,tot,pr[N],m,f[N],sum[N];bool is[N];
void getMu(int n)
{
mu[1]=1;
for(int i=2;i<=n;i++)
{
if(!is[i])
{
pr[++tot]=i;
mu[i]=-1;
}
for(int j=1;j<=tot&&i*pr[j]<=n;j++)
{
is[i*pr[j]]=1;
if(i%pr[j]==0)
{
mu[i*pr[j]]=0;
break;
}
mu[i*pr[j]]=-mu[i];
}
}
for(int i=1;i<=tot;i++)
{
for(int j=1;pr[i]*j<=n;j++)
{
f[pr[i]*j]+=mu[j];
}
}
for(int i=1;i<=n;i++)
{
sum[i]=sum[i-1]+f[i];
}
}
int main()
{
speed();
// freopen("in.in","r",stdin);
// freopen("out.out","w",stdout);
int T;
cin>>T;
getMu(1e7);
// for(int i=1;i<=10;i++)cout<<mu[i]<<" ";
// cout<<endl;
while(T--)
{
cin>>n>>m;
if(n>m)swap(n,m);
ll ans=0;
int l=1,r;
while(l<=n)
{
r=min(n/(n/l),m/(m/l));
ans+=1ll*(sum[r]-sum[l-1])*(long long)(n/l)*(m/l);
l=r+1;
}
cout<<ans<<endl;
}
return 0;
}
P3327 [SDOI2015] 约数个数和
首先知道一个性质:
$$\large d(ij)=\sum_{x|i}\sum_{y|j}[gcd(x,y)=1]$$
$$\large ans=\sum_{i=1}^n \sum_{j=1}^m \sum_{x|i}\sum_{y|j}[gcd(x,y)=1]$$
$$\large ans=\sum_{x=1}^n \sum_{y=1}^m \sum_{i=1}^{n} \sum_{j=1}^{m} [x|i][y|i][gcd(x,y)=1]$$
$$\large ans=\sum_{x=1}^n \sum_{y=1}^m \lfloor {\frac{n}{x}} \rfloor \lfloor {\frac{m}{y}} \rfloor [gcd(x,y)=1]$$
$$\large ans=\sum_{x=1}^n \sum_{y=1}^m \lfloor {\frac{n}{x}} \rfloor \lfloor {\frac{m}{y}} \rfloor \sum_{t|gcd(x,y)}\mu(t)$$ $$\large ans=\sum_{t=1}^n\mu(t) \sum_{x=1}^{\lfloor {\frac{n}{t}} \rfloor} \sum_{y=1}^{\lfloor {\frac{m}{t}} \rfloor} \lfloor {\frac{n}{xt}} \rfloor \lfloor {\frac{m}{yt}} \rfloor$$
后面两项我们可以预处理出来,用数论分块,\(O(n^2)->O(n\sqrt n)\)
查询\(O(T\sqrt n)\)
点击查看代码
#include <bits/stdc++.h>
#define speed() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ll long long
#define ull unsigned long long
#define lid (rt<<1)
#define rid (rt<<1|1)
// #define endl '\n'
//#define int long long
#define pb push_back
#define pii pair<int,int>
using namespace std;
const int N =5e4+5;
ll n,m,mu[N],tot,pr[N],sum[N],fx[N];bool is[N];
void getMu(int n)
{
mu[1]=1;
for(int i=2;i<=n;i++)
{
if(!is[i])
{
pr[++tot]=i;
mu[i]=-1;
}
for(int j=1;j<=tot&&pr[j]*i<=n;j++)
{
is[pr[j]*i]=1;
if(i%pr[j]==0)break;
mu[i*pr[j]]=-mu[i];
}
}
for(int i=1;i<=n;i++)
{
sum[i]=sum[i-1]+mu[i];
// cout<<mu[i]<<" ";
}
for(int i=1;i<=n;i++)
{
ll l=1,r;
ll res=0;
while(l<=i)
{
r=i/(i/l);
res+=1ll*(r-l+1ll)*(i/l);
l=r+1;
}
fx[i]=res;
}
}
ll solve(ll n,ll m)
{
if(n>m)swap(n,m);
int l=1,r;
ll res=0;
while(l<=n)
{
r=min(n/(n/l),m/(m/l));
res+=(sum[r]-sum[l-1])*fx[n/l]*fx[m/l];
l=r+1;
}
// cout<<res<<endl;
return res;
}
int main()
{
speed();
// freopen("in.in","r",stdin);
// freopen("out.out","w",stdout);
int T;
cin>>T;
// cout<<"*****"<<endl;
getMu(5e4);
// cout<<"*****"<<endl;
while(T--)
{
cin>>n>>m;
cout<<solve(n,m)<<endl;
}
return 0;
}
P3312 [SDOI2014] 数表
前置知识:\(\sigma\)除数函数
特殊的:
\(\sigma_1(n)即为n的因数和\)
\(\sigma_0(n)即为n的因数个数\)
本题即为
设\(T=dt\),则
如何预处理\(\sigma_1\),暴力就好了,\(O(\sum_{i=1}^n\frac{n}{i}=nl_nn)\)
按\(a\)排序,树状数组插入维护\(\sum_{t|T}\sigma_1{(\frac{T}{t})}\mu(t)\)即可
点击查看代码
#include <bits/stdc++.h>
#define speed() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ll long long
#define ull unsigned long long
#define lid (rt<<1)
#define rid (rt<<1|1)
// #define endl '\n'
#define int long long
#define pb push_back
#define pii pair<int,int>
using namespace std;
const int N =1e5+5,mod=(1LL<<31);
ll n,m,mu[N],tot,pr[N],sum[N],g[N];bool is[N];
struct Node
{
int id,z;
}d[N];
void getMu(int n)
{
mu[1]=1;
for(int i=2;i<=n;i++)
{
if(!is[i])
{
pr[++tot]=i;
mu[i]=-1;
}
for(int j=1;j<=tot&&pr[j]*i<=n;j++)
{
is[pr[j]*i]=1;
if(i%pr[j]==0)
{
break;
}
mu[i*pr[j]]=-mu[i];
}
}
for(int i=1;i<=n;i++)
{
sum[i]=sum[i-1]+mu[i];
// cout<<mu[i]<<" ";
}
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j+=i)
g[j]+=i;
for(int i=1;i<=n;i++)
{
d[i].id=i;d[i].z=g[i];
// cout<<d[i].id<<" "<<d[i].z<<endl;
}
sort(d+1,d+1+n,[&](Node a,Node b)
{
return a.z<b.z;
});
}
struct Query
{
int n,m,a,id;
bool operator < (const Query& A)const
{
return a<A.a;
}
}q[N];
struct BIT
{
ll c[N];
int lowbit(int x){return x&-x;}
void update(int x,int val){while(x<=1e5)c[x]+=val,c[x]%=mod,x+=lowbit(x);}
int query(int x){ll res=0;while(x)res+=c[x]%mod,x-=lowbit(x);return res;}
}bit;
void add(int t)
{
for(int i=1;i*t<=1e5;i++)bit.update(i*t,mu[i]*g[t]);
}
ll solve(ll n,ll m)
{
if(n>m)swap(n,m);
int l=1,r;
ll res=0;
while(l<=n)
{
r=min(n/(n/l),m/(m/l));
res+=(bit.query(r)-bit.query(l-1))*(n/l)*(m/l);
res%=mod;
// cout<<res<<endl;
l=r+1;
}
// cout<<res<<endl;
return (res+mod)%mod;
}
int Ans[N];
signed main()
{
speed();
// freopen("in.in","r",stdin);
// freopen("out.out","w",stdout);
int T;n=1e5;
cin>>T;
// cout<<T<<endl;
getMu(1e5);
// cout<<"*****"<<endl;
for(int i=1;i<=T;i++)
{
cin>>q[i].n>>q[i].m>>q[i].a;
q[i].id=i;
// cout<<solve(n,m)<<endl;
}
sort(q+1,q+1+T);
int now=0;
for(int i=1;i<=T;i++)
{
while(d[now+1].z<=q[i].a&&now<=1e5)now++,add(d[now].id);
Ans[q[i].id]=solve(q[i].n,q[i].m);
}
for(int i=1;i<=T;i++)
{
cout<<Ans[i]<<endl;
}
return 0;
}
P3704 [SDOI2017] 数字表格
设\(T=dt\)
点击查看代码
#include <bits/stdc++.h>
#define speed() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ll long long
#define ull unsigned long long
#define lid (rt<<1)
#define rid (rt<<1|1)
// #define endl '\n'
//#define int long long
#define pb push_back
#define pii pair<int,int>
using namespace std;
const int N = 1e6+5,mod=1e9+7;
ll mu[N],pr[N],tot,sum[N];bool is[N];
ll f[N],n,m,g[N],ans[N],F[N];
ll qpow(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1)ans=ans*a%mod;
b>>=1;a=a*a%mod;
}
return ans;
}
void getMu(int n)
{
mu[1]=1;f[1]=f[2]=1;F[1]=F[0]=1;g[1]=1;
for(int i=2;i<=n;i++)
{
f[i]=(f[i-1]+f[i-2])%mod;
g[i]=qpow(f[i],mod-2);
F[i]=1;
if(!is[i])
{
pr[++tot]=i;mu[i]=-1;
}
for(int j=1;j<=tot&&pr[j]*i<=n;j++)
{
is[pr[j]*i]=1;
if(i%pr[j]==0)break;
mu[pr[j]*i]=-mu[i];
}
}
// for(int i=1;i<=n;i++)sum[i]=sum[i-1]+mu[i];
}
ll solve(ll n,ll m)
{
ll l=1,r=0;
if(n>m)swap(n,m);
ll res=1;
while(l<=n)
{
r=min(n/(n/l),m/(m/l));
res=res*qpow(F[r]*qpow(F[l-1],mod-2)%mod,(n/l)*(m/l)%(mod-1))%mod;
l=r+1;
}
return res;
}
int main()
{
speed();
// freopen("in.in","r",stdin);
// freopen("out.out","w",stdout);
getMu(1e6);
int T;
cin>>T;
for(int i=1;i<=1e6;i++)
{
if(!mu[i])continue;
for(int j=i;j<=1e6;j+=i)
F[j]=(F[j]*(mu[i]==1?f[j/i]:g[j/i]))%mod;
}
for(int i=2;i<=1e6;i++)
{
F[i]=F[i-1]*F[i]%mod;
// cout<<F[i]<<endl;
}
// for(int i=1;i<=1e6;i++)ans[i]=ans[i-1]*g[i]%mod;
while(T--)
{
cin>>n>>m;
cout<<(solve(n,m)+mod)%mod<<endl;
}
return 0;
}
P4449 于神之怒加强版
你尝试把他变成 \(\varphi\),但你发现答案成了这样:
但是没用
设
那么答案就成了:
先说结论:
如何证明:因为线性筛,就设\(b\)为质数显然若\(gcd(a,b)=1,那么G(ab)=G(a)\times G(b)\)
若\(gcd(a,b)\ne 1\),且\(b\)为质数,则\(a\)中含有\(b\)这个质因子,唯一分解定理知道\(a=\prod p_i^{c_i}\)
那么等价于\(G(b^{c+1})=G(b^c)\times x(x未知)\)
有刚才结论\(G(p^{x+1})=p^{(x+1)k}-p^{xk}\),所以两边一消,\(x=b^k\)
点击查看代码
#include <bits/stdc++.h>
#define speed() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ll long long
#define ull unsigned long long
#define lid (rt<<1)
#define rid (rt<<1|1)
// #define endl '\n'
//#define int long long
#define pb push_back
#define pii pair<int,int>
using namespace std;
const int N = 5e6+5,mod=1e9+7;
ll mu[N],pr[N],tot,sum[N];bool is[N];
ll n,m,g[N],k;
ll qpow(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1)ans=ans*a%mod;
b>>=1;a=a*a%mod;
}
return ans;
}
void getMu(int n)
{
mu[1]=1;g[1]=1;
for(int i=2;i<=n;i++)
{
if(!is[i])
{
pr[++tot]=i;mu[i]=-1;
g[i]=(qpow(i,k)-1+mod)%mod;
}
for(int j=1;j<=tot&&pr[j]*i<=n;j++)
{
is[pr[j]*i]=1;
if(i%pr[j]==0)
{
g[pr[j]*i]=g[i]*(g[pr[j]]+1)%mod;
break;
}
mu[pr[j]*i]=-mu[i];
g[pr[j]*i]=g[pr[j]]*g[i]%mod;
}
}
for(int i=1;i<=n;i++)sum[i]=(sum[i-1]+g[i])%mod;
}
ll solve(ll n,ll m)
{
ll l=1,r=0;
if(n>m)swap(n,m);
ll res=0;
while(l<=n)
{
r=min(n/(n/l),m/(m/l));
res=(res+(sum[r]-sum[l-1]+mod)%mod*(n/l)%mod*(m/l)%mod)%mod;
l=r+1;
}
return res;
}
int main()
{
speed();
// freopen("in.in","r",stdin);
// freopen("out.out","w",stdout);
int T;
cin>>T>>k;
getMu(5e6);
while(T--)
{
cin>>n>>m;
cout<<(solve(n,m)+mod)%mod<<endl;
}
return 0;
}
公约数和
点击查看代码
#include <bits/stdc++.h>
#define speed() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ll long long
#define ull unsigned long long
#define lid (rt<<1)
#define rid (rt<<1|1)
// #define endl '\n'
//#define int long long
#define pb push_back
#define pii pair<int,int>
using namespace std;
const int N = 5e6+5,mod=1e9+7;
ll mu[N],pr[N],tot,sum[N];bool is[N];
ll n,m,g[N],k;
ll qpow(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1)ans=ans*a%mod;
b>>=1;a=a*a%mod;
}
return ans;
}
void getMu(int n)
{
mu[1]=1;
for(int i=2;i<=n;i++)
{
if(!is[i])
{
pr[++tot]=i;mu[i]=-1;
}
for(int j=1;j<=tot&&pr[j]*i<=n;j++)
{
is[pr[j]*i]=1;
if(i%pr[j]==0)
{
g[pr[j]*i]=g[i]*(g[pr[j]]+1)%mod;
break;
}
mu[pr[j]*i]=-mu[i];
}
}
for(int i=1;i<=n;i++)sum[i]=(sum[i-1]+mu[i]);
}
ll solve(ll n,ll d)
{
ll l=1,r=0;
n/=d;
ll res=0;
while(l<=n)
{
r=n/(n/l);
res=(res+(sum[r]-sum[l-1])*(n/l)*(n/l));
l=r+1;
}
return res;
}
int main()
{
speed();
// freopen("in.in","r",stdin);
// freopen("out.out","w",stdout);
cin>>n;
getMu(n);
ll ans=0;
for(int d=1;d<=n;d++)
{
ans+=d*solve(n,d);
}
cout<<(ans-(n*(n+1))/2)/2;
return 0;
}
P1829 [国家集训队] Crash的数字表格 / JZPTAB
式子好推,不过测试点造的很强,每个括号都不能落下
点击查看代码
#include <bits/stdc++.h>
#define speed() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ll long long
#define pb push_back
#define ull unsigned long long
using namespace std;
#define int long long
const ll N=1e7+5,mod=20101009 ;
ll a,b;
bool is[N];ll pr[N],mu[N],tot,sum[N];
ll f[N],n,m;
void getMu(int n)
{
mu[1]=1;f[1]=1;
for(ll i=2;i<=n;i++)
{
if(!is[i])
{
pr[++tot]=i;mu[i]=-1;
f[i]=(1-i)%mod;
}
for(int j=1;j<=tot&&i*pr[j]<=n;j++)
{
is[i*pr[j]]=1;
if(i%pr[j]==0)
{
f[i*pr[j]]=f[i];
break;
}
mu[i*pr[j]]=-mu[i];
f[i*pr[j]]=f[i]*f[pr[j]]%mod;
// f[i*pr[j]]=max<ll>(1,f[i]);
}
}
for(ll i=1;i<=n;i++)
{
sum[i]=(sum[i-1]+1ll*f[i]*i%mod)%mod;
sum[i]%=mod;
// if(i<=1e4)cout<<sum[i]<<" ";
}
}
ll S(ll x)
{
return (x*(x+1)/2)%mod;
}
ll solve(ll n,ll m)
{
if(n>m)swap(n,m);
ll l=1,r;ll ans=0;
while(l<=n)
{
r=min(n/(n/l),m/(m/l));
ans+=1ll*((sum[r]-sum[l-1]+mod)%mod)*((S(n/l)*S(m/l))%mod)%mod;//这里不括括号的话会炸掉,优先级算完一步就取一次模
ans=(ans+mod)%mod;
// cout<<ans<<endl;
l=r+1;
}
return ans;
}
signed main()
{
speed();
// freopen("in.in","r",stdin);
// freopen("out.out","w",stdout);
// int T;
// cin>>T;
getMu(1e7);
// cout<<"*******"<<endl;
cin>>n>>m;
// cout<<n<<" "<<m<<endl;
cout<<(solve(n,m)+mod)%mod<<endl;
return 0;
}