【洛谷P2155】[SDOI2008]沙拉公主的困惑

题目描述

大富翁国因为通货膨胀,以及假钞泛滥,政府决定推出一项新的政策:现有钞票编号范围为1到N的阶乘,但是,政府只发行编号与M!互质的钞票。房地产第一大户沙拉公主决定预测一下大富翁国现在所有真钞票的数量。现在,请你帮助沙拉公主解决这个问题,由于可能张数非常大,你只需计算出对R取模后的答案即可。R是一个质数。

输入输出格式

输入格式:

第一行为两个整数T,R。R<=10^9+10,T<=10000,表示该组中测试数据数目,R为模 后面T行,每行一对整数N,M,见题目描述 m<=n

输出格式:

共T行,对于每一对N,M,输出1至N!中与M!素质的数的数量对R取模后的值

输入输出样例

输入样例#1:
1 11
4 2
输出样例#1:
1

数据范围:
对于100%的数据,1 < = N , M < = 10000000

分析(转载)

原文地址

首先,我们来引出一个定理
如果$a$与$b$互质,那么$b*k+a$也与$b$互质。证明和证明$gcd$的证明类似。
反过来,我们也可以用$gcd$证明,
因为$gcd(a,b)=1$,所以$gcd(a\%b,b)=1$
因为$a\%b=a-k*b$,故$gcd(a-k*b,b)=1$,及$a-k*b$与$b$互质。
根据这个特性,并且$n>=m$,所以可以将$n!$分成若干段,每段为$m!$,每一段中与$m!$互质的个数都是相等的且等于1到$m!$中与$m!$互质的个数
我们可以得到式子

$ans={frac{n!}{m!}*phi(m!)}$

进一步拆开,我们可以得到 (假设$p$为$m!$的质因数,很容易可以知道,$p$就是所有小于$m$的素数,$r$为质因数个数)

$ans={frac{n!}{m!}*m!*frac{prod limits_{i=1}^{r}(p_i-1)}{prodlimits_{i=1}^{r}p_i } o ans=n!*frac{prod limits_{i=1}^{r}(p_i-1)}{prodlimits_{i=1}^{r}p_i } }$

因为$ans$ 要$mod R$,所以我们也要算1到m的逆元,在累乘$prodlimits_{i=1}^{r}p_i$ ,乘的是$p_i$ 的逆元。 有多组询问,我们得先预处理一些数据,累乘的时候要$%R$ 我们令$k[i] = i!$ ,$inv[i]$为$i$的逆元,

$f1[i]= {prodlimits_{a=1}^{i}(p_a-1)}$
$f2[i]={prodlimits_{a=1}^{i}p_a}$
$ans=f1[m]*f2[m]*k[n]$
先预处理O()答案,对于询问O(1)出解

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=10000000+1;
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 T,p,n,m,tot;
int fac[maxn],inv[maxn],ans[maxn],prime[maxn];
bool isp[maxn];
void prework()
{
    fac[1]=1; inv[1]=1; ans[1]=1;
    for(int i=2;i<=maxn;i++)
    fac[i]=((ll)fac[i-1]*(i%p))%p;
    for(int i=2;i<=maxn;i++)
    inv[i]=(ll)(p-p/i)*inv[p%i]%p;
    for(int i=2;i<=maxn;i++)
    {
        if(!isp[i]) prime[++tot]=i;
        for(int j=1;j<=tot&&prime[j]*i<=maxn;j++)
        {
            isp[prime[j]*i]=1;
            if(i%prime[j]==0) break;
        }
    }
    for(int i=2;i<=maxn;i++)
    {
        if(!isp[i])
            ans[i]=(ll)ans[i-1]*(i-1)%p*inv[i%p]%p;
        else ans[i]=ans[i-1];
    }
}
int main()
{
    T=read();p=read();
    prework();
    while(T--)
    {
        n=read();m=read();
        printf("%d
",(ll)fac[n]*ans[m]%p);
    }
    return 0;
}
欢迎转载,转载请注明出处!
原文地址:https://www.cnblogs.com/huihao/p/7501371.html