[bzoj2186][Sdoi2008]沙拉公主的困惑

来自FallDream的博客,未经允许,请勿转载,谢谢。


 

题意:T组数据,每次求[1,n!]里有多少个与m!互质的数。  T<=10000 n,m<=10^7

 

一开始想着乱容斥啥的 根本不可做...但是实际上没那么复杂。

考虑把[1,n!]按照取余m!的不同分成m!组,发现要么全都互质,要么全都不互质,所以我们只要求[1,m!]里跟m!互质的数就好了,也就是求$\varphi(m!)$

那么这样就很简单了,根据欧拉函数的一套理论(#滑稽),我们先筛出质数,然后$\varphi(m!)=m!*\prod{\frac{i-1}{i}}$,其中$1\leqslant i\leqslant m$且i是质数。

答案就是$\frac{n!}{m!}\varphi(m!)$

实现上还要线性求个逆元,这都很简单啦。

然后考虑R<=m的情况,貌似出题人没有考虑到或者没有写出来,虽然不判断都能过,但是直接这么算是不行的。

发现p在筛的时候会被除掉,所以在算阶乘的时候忽略它,然后在算的时候也不算它的逆元,可能就行了?

貌似好麻烦,懒得实现了。

#include<iostream>
#include<cstdio>
#define MN 10000000
using namespace std;
inline int read()
{
    int x = 0 , f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}

int s[MN+5],T,mod,f[MN+5],num=0,inv[MN+5],p[MN+5];
bool b[MN+5];

int main()
{
    T=read();mod=read();
    f[1]=1,p[0]=p[1]=inv[0]=inv[1]=1;
    for(register int i=2;i<=MN;++i)
    {
        if(!b[i]) s[++num]=i;
        for(int j=1;s[j]*i<=MN;++j)
        {
            b[s[j]*i]=1;
            if(i%s[j]==0) break;
        }
    }
    for(register int i=2;i<=MN;++i)
        p[i]=1LL*p[i-1]*i%mod,inv[i]=1LL*(mod-mod/i)*inv[mod%i]%mod;
    for(register int i=2;i<=MN;++i)
        f[i]=(b[i]?f[i-1]:(1LL*f[i-1]*(i-1)%mod*inv[i]%mod));
    for(register int i=1;i<=T;++i)
    {
        int n=read(),m=read();
        printf("%d\n",1LL*p[n]*f[m]%mod);
    }
    return 0;
}

 

posted @ 2017-04-19 00:02  FallDream  阅读(197)  评论(0编辑  收藏  举报