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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章