E. Devu and Birthday Celebration
题意:有q次询问,每次询问n,k,要你把n分成 k 块,任意两块gcd=1的方案数。n,k,q最大均为1e5。
设f(n,k,i)为把n分成k块,任意两块gcd为 i 的方案数。
设F(n,k,i)为把n分成k块,任意两块gcd为 i 的倍数的方案数。
很显然:
不难发现f(n,k,d)=f(n/d,k,1),则:
反演一下:
然后就可以水了
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+10,mod=1e9+7;
ll p[maxn],inv[maxn];
int pri[maxn],vis[maxn],mu[maxn],cnt;
vector<int>G[maxn];
ll ksm(ll x,int y)
{
ll res=1;
while(y)
{
if(y&1)res=res*x%mod;
x=x*x%mod;
y/=2;
}
return res;
}
void init(int n)
{
mu[1]=1;
for(int i=2;i<=n;i++)
{
if(!vis[i])pri[++cnt]=i,mu[i]=-1;
for(int j=1;j<=cnt&&i*pri[j]<=n;j++)
{
vis[i*pri[j]]=1;
if(i%pri[j]==0)break;
else mu[i*pri[j]]=-mu[i];
}
}
inv[0]=p[0]=1;
for(int i=1;i<=n;i++)
p[i]=p[i-1]*i%mod,inv[i]=ksm(p[i],mod-2);
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j+=i)
G[j].push_back(i);
}
ll cal(int n,int m)
{
n--,m--;
if(n<m)return 0ll;
return p[n]*inv[n-m]%mod*inv[m]%mod;
}
void add(ll &x,ll y)
{
x=(x+y+mod)%mod;
}
int main()
{
init(1e5);
int T,n,k;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&k);
ll ans=0;
for(int i=0;i<G[n].size();i++)
add(ans,mu[n/G[n][i]]*cal(G[n][i],k));
printf("%lld\n",ans);
}
}