题意
次询问,每次给出,求存在多少个长度为的序列满足:
做法一:dp
- 首先考虑没有gcd的条件,根据隔板法可得答案为
- 正难则反,看看有没有办法求出的答案,考虑枚举gcd,提出来就变成了且,那么可以得出递推式,状态数为的因子数量,而,因此可以对于每个记忆化搜索,复杂度为,最坏为
- 总复杂度:
- 在线做法需要卡常,可以考虑离线对于每个单独算会快很多
cpp
vector<int> fac[N];
int fz[N],fm[N];
int qpow(int a,int b){
int ans=1;
for(;b;b>>=1){
if(b&1)ans=ans*a%mod;
a=a*a%mod;
}
return ans;
}
void init(){
for(int i=2;i<N;i++){
for(int j=i;j<N;j+=i){
fac[j].push_back(i);
}
}
for(int i=(fz[0]=1);i<N;i++)fz[i]=fz[i-1]*i%mod;
fm[N-1]=qpow(fz[N-1],mod-2);
for(int i=N-2;~i;i--)fm[i]=fm[i+1]*(i+1)%mod;
}
int C(int n,int m){
if(n<m)return 0;
return fz[n]*fm[m]%mod*fm[n-m]%mod;
}
int dp[N];
void solve(){
init();
memset(dp,-1,sizeof(dp));
int q;
cin>>q;
while(q--){
int n,m;
vector<int> tmp;
function<int(int)> dfs=[&](int i){
if(i<m)return 0ll;
if(~dp[i])return dp[i];
int x=C(i-1,m-1);
for(auto p:fac[i])x=(x-dfs(i/p)+mod)%mod;
tmp.push_back(i);
return dp[i]=x;
};
cin>>n>>m;
cout<<dfs(n)<<"\n";
for(auto x:tmp)dp[x]=-1;
}
}做法二:莫比乌斯反演
- 考虑对式子移项得:
- 预处理出因子和莫比乌斯函数,总时间复杂度
cpp
vector<int> fac[N];
int fz[N],fm[N],mu[N];
int qpow(int a,int b){
int ans=1;
for(;b;b>>=1){
if(b&1)ans=ans*a%mod;
a=a*a%mod;
}
return ans;
}
void init(){
for(int i=1;i<N;i++){
for(int j=i;j<N;j+=i){
fac[j].push_back(i);
}
}
mu[1]=1;
for(int i=2;i<N;i++){
int p=fac[i][1];
if(i/p%p==0)mu[i]=0;
else mu[i]=-mu[i/p];
}
for(int i=(fz[0]=1);i<N;i++)fz[i]=fz[i-1]*i%mod;
fm[N-1]=qpow(fz[N-1],mod-2);
for(int i=N-2;~i;i--)fm[i]=fm[i+1]*(i+1)%mod;
}
int C(int n,int m){
if(n<m)return 0;
return fz[n]*fm[m]%mod*fm[n-m]%mod;
}
void solve(){
init();
int q;
cin>>q;
while(q--){
int n,m;
vector<int> tmp;
int ans=0;
cin>>n>>m;
for(auto d:fac[n]){
ans=(ans+C(n/d-1,m-1)*mu[d]%mod+mod)%mod;
}
cout<<ans<<"\n";
}
}