BZOJ2186:[Sdoi2008]沙拉公主的困惑

Description

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2186

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

Input

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

Output

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

Sample Input

1 11
4 2

Sample Output

1

数据范围:

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

题解

本题其实不算一个难题,题意显然就是要求在N!内与M!互质的数的个数,我们先来看在M!内与M!互质的数,很显然就是一个欧拉函数,可以线性的求出,那么在N!内呢?显然如果i与M!是互质的,那么i+M!也与M!互质,i+2×M!也与M!互质,那么在N!内与M!互质的个数即为ϕ(M!)×N!M!mod(r) .

又由于ϕ(M!)=M!×p11p1×p22p2×... ,

所以又可以化为N!×Π(pi1)/pi(modr)

其中pi指的是M!的不同质因子,显然这个式子中,n!mod(r) 可以O(n)预处理,而Π(pi1)/pi 也是可以O(n)预处理的.

所以可以O(n)预处理,O(1)查询;
下面我们来看看Π(pi1)/pi 的求法,假设我们令a[m]表示Π(pi1)/pi(modr) ,pi为m!的质因子,那么我们可以分情况讨论,当m为质数时,那么m!的质因子与(m-1)!的质因子只有一个不同,那就是多了一个m,所以此时a[m]=a[m1]×m .而如果m不为质数,那么m!的质因子与(m-1)!的质因子是完全一样的,不同的只是某个因子的指数,所以此时a[m]=a[m1] ,至此,Π(pi1)/pi 我们也在线性时间下算出了,问题也就解决了。

代码

/**************************************************************
    Problem: 2186
    User: zyg0121
    Language: C++
    Result: Accepted
    Time:6408 ms
    Memory:130196 kb
****************************************************************/

#include<cstdio>
#include<cmath>
#include<ctime>
#include<cstring>
#include<iostream>
#include<algorithm>
const int inf = 1000000000;
const int N = 10000000;
typedef long long ll ;
using namespace std;

int read() {
    int x=0;
    char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9') {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x;
}

int T,R,n,m,cnt;
int fac[10000005],ine[10000005],pri[500005],ans[10000005];
bool mark[10000005];

void exgcd(int a,int b,int &x,int &y) {
    if(b==0) {
        x=1;
        y=0;
        return;
    }
    exgcd(b,a%b,x,y);
    int t=x;
    x=y;
    y=t-a/b*y;
}
int getine(int t) {
    int x,y;
    exgcd(t,R,x,y);
    return (x%R+R)%R;
}

void pre() {
    fac[1]=1;
    for(int i=2; i<=N; i++)fac[i]=(ll)fac[i-1]*i%R;
    ine[1]=1;
    for(int i=2; i<=N; i++) {
        if(!mark[i])pri[++cnt]=i,ine[i]=getine(i);
        for(int j=1; pri[j]*i<=N&&j<=cnt; j++) {
            mark[pri[j]*i]=1;
            if(i%pri[j]==0)break;
        }
    }
    ans[1]=1;
    for(int i=2; i<=N; i++) {
        ans[i]=ans[i-1];
        if(!mark[i])ans[i]=(ll)ans[i]*(i-1)%R*ine[i]%R;
    }
}
int main() {
    T=read();
    R=read();
    pre();
    while(T--) {
        n=read();
        m=read();
        printf("%d\n",(ll)fac[n]*ans[m]%R);
    }
    return 0;
}

下面有一份80分的在线代码,最后几个点RE,烦请大神帮帮蒟蒻吧!!!

#include <cstdio>
#include <iostream>
#include <cmath>
using namespace std;
const int size = 1000000+50;
typedef long long LL;

LL f[size],p[size],a[size];
int now = 1,top = 1,tot = 1;
int r,n,m;
int prime[500050];
bool v[size];

void isprime() {
    LL ans=0;
    for(int i=2; i<=size; i++) {
        if(!v[i]) prime[++ans]=i;
        for(int j=1; j<=ans&&i*prime[j]<=size; j++) {
            v[i*prime[j]]=true;
            if(i%prime[j]==0)
                break;
        }
    }
    return ;
}

void mod(int t) {
    while(top<t)
        f[++top]=f[top-1]*(top%r)%r;
}

LL inv (int t) {
    if(t<=tot)
        return p[t];
    else
        while(tot<t)
            p[++tot]=(r-r/tot)*p[r%tot]%r;
    return p[tot];
}

LL ans(int t) {
    if(t<=now)
        return a[t];
    while(now<t) {
        a[++now]=a[now-1];
        if(!v[now])
            a[now]=a[now]*(now-1)%r*inv(now%r)%r;
    }
    return a[t];
}

int main() {
    int T;
    scanf("%d%d",&T,&r) ;
    isprime();
    a[1]=1;p[0]=0;
    p[1]=1;f[1]=1;
    while(T--) {
        scanf("%d%d",&n,&m);
        if(n>top)
            mod(n);
        printf("%lld\n",f[n]*ans(m)%r);
    }
    return 0;
}
发布了39 篇原创文章 · 获赞 7 · 访问量 1万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章