1. 程式人生 > >洛谷 P2155 [SDOI2008]沙拉公主的困惑 解題報告

洛谷 P2155 [SDOI2008]沙拉公主的困惑 解題報告

algo upper 題目 per npr als rod i++ 可能

P2155 [SDOI2008]沙拉公主的困惑

題目描述

大富翁國因為通貨膨脹,以及假鈔泛濫,政府決定推出一項新的政策:現有鈔票編號範圍為\(1\)\(N\)的階乘,但是,政府只發行編號與\(M!\)互質的鈔票。房地產第一大戶沙拉公主決定預測一下大富翁國現在所有真鈔票的數量。現在,請你幫助沙拉公主解決這個問題,由於可能張數非常大,你只需計算出對\(R\)取模後的答案即可。\(R\)是一個質數。

輸入輸出格式

輸入格式:

第一行為兩個整數\(T\)\(R\)\(R \le 10^9+10\)\(T \le 10000\),表示該組中測試數據數目,\(R\)為模。
後面\(T\)行,每行一對整數\(N\)

\(M\),見題目描述\(m \le n\)

輸出格式:

\(T\)行,對於每一對\(N\)\(M\),輸出\(1\)\(N!\)中與\(M!\)素質的數的數量對\(R\)取模後的值


題意:

\(\frac{N!}{M!} \varphi(M!)\)

註意,這裏每\(M!\)段互質的個數是一樣的,可以用\(gcd(a,b)=gcd(b,a-b)\)證明,而\(M!|N!\),才有了上面一個式子

按照歐拉函數定義式化簡一下

\(N!\prod_{p|m!}\frac{p-1}{p}\),\(p\)質數

很明顯把兩個乘積項預處理一下,右邊可以二分找一下

註意預處理階乘時不能簡單的預處理,因為如果\(R \le N\)

,那麽就輸出\(0\)了,事實上不一定輸出\(0\)

先把\(R\)的次數拿出來,然後再搞一搞

是不是感覺這樣是\(O(NlogR)\)的?事實上不是

這個和\(N!\)\(R\)的次數直接相關,設\(f(n)\)代表\(n!\)中有多少個因子\(r\)

顯然有\(f(n)=f(\lfloor \frac{n}{r} \rfloor)+\lfloor \frac{n}{r} \rfloor\)

結果是\(O(n)\)

事實上跑上去有點慢。。


Code:

#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
const int N=1e7;
const int M=7e5;
bool ispri[N+10];
int pri[M],cnt,fr[M],num[M],fac[N+10],numfac[N+10],r,t,n,m;
int mul(int a,int b)
{
    ll c=(ll)(a)*b%r;
    return (int)(c);
}
int inv(int b,int k)
{
    int f=1;
    while(k)
    {
        if(k&1) f=mul(f,b);
        b=mul(b,b);
        k>>=1;
    }
    return f;
}
void init()
{
    memset(ispri,true,sizeof(ispri));
    for(int i=2;i<=N;i++)
    {
        if(ispri[i])
            pri[++cnt]=i;
        for(int j=1;j<=cnt&&pri[j]*i<=N;j++)
        {
            ispri[pri[j]*i]=false;
            if(i%pri[j]==0) break;
        }
    }

    fr[0]=1;
    for(int i=1;i<=cnt;i++)
    {
        num[i]=num[i-1];
        fr[i]=fr[i-1];
        if(pri[i]-1==r)
            ++num[i],fr[i]=mul(fr[i],inv(pri[i],r-2));
        else if(pri[i]==r)
            --num[i],fr[i]=mul(fr[i],pri[i]-1);
        else
            fr[i]=mul(fr[i],mul(inv(pri[i],r-2),pri[i]-1));
    }

    fac[0]=1;
    for(int i=1;i<=N;i++)
    {
        fac[i]=fac[i-1];
        numfac[i]=numfac[i-1];
        int d=i;
        while(d%r==0) ++numfac[i],d/=r;
        fac[i]=mul(fac[i],d);
    }
}
int main()
{
    scanf("%d%d",&t,&r);
    init();
    while(t--)
    {
        scanf("%d%d",&n,&m);
        int pos=std::upper_bound(pri+1,pri+1+cnt,m)-pri-1;
        int ans=mul(fac[n],fr[pos]);
        if(num[pos]+numfac[n]==0) printf("%d\n",ans);
        else printf("0\n");
    }
    return 0;
}

2018.9.27

洛谷 P2155 [SDOI2008]沙拉公主的困惑 解題報告