[题解]luogu_P2155_BZOJ_2186沙拉公主的困惑

题意求1~N!中与M!互质的数的个数,

首先证明gcd(a,b)=1时gcd(a-kb,b)=1

gcd(a,b)=1

gcd(a%b,b)=1

gcd(a-kb,b)=1

即a-kb与b互质

这样由于n!一定是m!的倍数,所以如果把n!分成很多段m!的和:1~m!,m!~2m!......

对于每一段的每个答案gcd(x,m!)=1时,也有gcd(x+km!,m!)=1

所以每段的答案都是一样的

这样答案变成了n!/m! * phi(m!)

对于phi(m!)用计算公式展开:

这里可以递推出相关的东西和逆元之类的,然而其实还可以继续简化运算

其实也只是不用求逆元了而已吧

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=10000009;
int n,m;
ll mod;
ll fac[maxn],f1[maxn],f2[maxn];
int prime[maxn];
bool ck[maxn];
void init(){
    int tot=0;
    memset(ck,0,sizeof(ck));
    ck[0]=ck[1]=1;
    for(int i=2;i<=maxn;i++){
        if(!ck[i])prime[++tot]=i;
        for(int j=1;j<=tot;j++){
            if(i*prime[j]>maxn)break;
            ck[i*prime[j]]=1;
            if(i%prime[j]==0)break;
        }
    }
}
ll qpow(ll a,ll b){
    ll base=a,ans=1;
    while(b){
        if(b&1)ans=(ans*base)%mod;
        base=(base*base)%mod;
        b>>=1;
    }
    return ans%mod;
}
int main(){
    int T;
    scanf("%d%lld",&T,&mod);
    init();
    fac[1]=f1[1]=f2[1]=1ll;
    for(int i=2;i<maxn;i++){
        fac[i]=fac[i-1]*i%mod;
        if(!ck[i])
        f1[i]=f1[i-1]*(i-1)%mod,f2[i]=f2[i-1]*i%mod;
        else
        f1[i]=f1[i-1],f2[i]=f2[i-1];
    }
    while(T--){
        scanf("%d%d",&n,&m);
        printf("%lld\n",((fac[n]*f1[m])%mod)*qpow(f2[m],mod-2)%mod);
    }
}

 

posted @ 2019-05-16 15:08  羊肉汤泡煎饼  阅读(114)  评论(0编辑  收藏  举报