莫比乌斯容斥:Codeforces Round #251 (Div. 2)

版权声明:原创文章,转载要注明作者哦 https://blog.csdn.net/DYT_B/article/details/81556666

题目描述戳这里
题解:
这题是莫比乌斯函数的一个比较好的应用。
我们假设没有gcd为1这个限制条件,那么直接C(n-1,r-1)就行了
这样求出来的其实是大于等于gcd=1的方案总数。
而我们要求的其实是恰好gcd=1的方案数。
那么这就是容斥的典型应用。
但是我们容斥的其实是有gcd中有几个不同的素因子(同一个因子出现多次已经在在这个素因子出现一次的情况中统计过了)。
那么我们只要考虑gcd中出现了几个不同的素因子,0个(最大公因数==1)+,1个-,2个+,…
那么这个用莫比乌斯函数就很好处理了。
但是这题十分卡时间,一定要记忆化一下答案,才不会超时。。。

代码如下:

#include<cstdio>
#include<string>
#include<cstring>
#include<map>
#define ll long long
using namespace std;
const int maxn=100005,tt=1e9+7;
int T,n,m,mu[maxn],su[maxn],n1;
ll ans,fac[maxn],inv[maxn];
typedef pair<int, int> Pii;
bool vis[maxn];
map <Pii,ll> hsh;
void make_mu(){
    vis[1]=1; mu[1]=1;
    for (int i=2;i<=100000;i++) {
        if (!vis[i]) mu[i]=-1,su[++n1]=i;
        for (int j=1;j<=n1&&su[j]*i<=100000;j++) {
            vis[i*su[j]]=1; 
            if (i%su[j]==0) {mu[i*su[j]]=0; break;}
            else mu[i*su[j]]=-mu[i];
        }
    }
}
ll qsm(ll x,int y){
    ll ret=1;
    while (y){
        if (y%2) ret=ret*x%tt;
        x=x*x%tt,y/=2;
    }
    return ret;
}
ll C(int x,int y){return fac[x]*inv[y]%tt*inv[x-y]%tt;}
int main(){
    scanf("%d",&T);
    fac[0]=inv[0]=1;
    for (int i=1;i<=100000;i++) fac[i]=fac[i-1]*i%tt,inv[i]=qsm(fac[i],tt-2);
    make_mu();
    while (T--) {
        scanf("%d %d",&n,&m); ans=0;
        if (hsh.find(Pii(n,m))!=hsh.end()) {printf("%lld\n",hsh[Pii(n,m)]); continue;}
        for (int i=1;i<=n/m;i++) {
            if (n%i) continue;
            ans+=mu[i]*C(n/i-1,m-1);
            if (ans<0) ans+=tt;
            if (ans>=tt) ans-=tt;
        }
        hsh[Pii(n,m)]=ans;
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/DYT_B/article/details/81556666
今日推荐