• 题解 Luogu P2155 [SDOI2008]沙拉公主的困惑


    传送门

    发现好多人的做法并不对......


    【分析】

    先化简一波式子:

    (quad Ans)
    (displaystyle =sum_{i=1}^{N!}[gcd(i, M!)=1])
    (displaystyle =sum_{i=1}^{N!}sum_{dmid iwedge dmid (M!)}oldsymbol mu(d))
    (displaystyle =sum_{dmid (M!)}oldsymbol mu(d)sum_{i=1}^{N!}[dmid i])

    由于 (Mleq N)((M!)mid (N!))(dmid (N!))

    ( herefore Ans)
    (displaystyle =sum_{dmid (M!)}oldsymbol mu(d){N!over d})
    (displaystyle ={N!over M!}sum_{dmid (M!)}oldsymbol mu(d){M!over d})
    (displaystyle ={N!over M!}(oldsymbol mu*oldsymbol {id})(M!))
    (displaystyle ={N!over M!}oldsymbol varphi(M!))
    (displaystyle ={N!over M!}cdot M!cdot prod_{pmid (M!)}{p-1over p})
    (displaystyle =N!prod_{pleq M}{p-1over p})

    答案要求对 (R) 取模,但并没有表明 (R) 是否严格大于 (M)

    故类似于 exLucas 的想法:设 (f_R(n)) 表示 (n) 去除 (R) 质因子后,剩余质因子的乘积;(g_R(n)) 表示 (n)(R) 质因子的次数

    (displaystyle n=f_R(n)cdot R^{displaystyle g_R(n)})

    代入所求式子得:(displaystyle Ans={f_R(N!)cdot f_R(prod_{pleq M}(p-1))over f_R(prod_{pleq M}p)}cdot R^{displaystyle g_R(N!)+g_R(prod_{pleq M}(p-1))-g_R(prod_{pleq M}p)})

    (displaystyle g_R(N!)+g_R(prod_{pleq M}(p-1))-g_R(prod_{pleq M}p)>0) 时,(Ansequiv 0pmod R)

    (displaystyle g_R(N!)+g_R(prod_{pleq M}(p-1))-g_R(prod_{pleq M}p)=0) 时,(displaystyle Ansequiv f_R(N!)cdot f_R(prod_{pleq M}(p-1)) cdot f_R^{-1}(prod_{pleq M}p)pmod R)


    现考虑如何线性求解三个数字的 (f_R)(g_R)

    对于 (N!)(egin{cases} f_R(N!)equiv f_R((N-1)!)cdot f_R(N)pmod R \ g_R(N!)equiv g_R((N-1)!)+g_R(N)pmod R end{cases})

    其中,(f_R(N))(g_R(N)) 可以通过对 (N) 暴力拆解得到,复杂度为 (T(N)=O(N)+O(N)+O({Nover R})+O({Nover R^2})+O({Nover R^3})+cdots =O(N)+O({Nover 1-{1over R}})=O(N))

    对于 (displaystyle prod_{pleq M} p)(egin{cases} egin{cases} displaystyle f_R(prod_{pleq M}p)equiv f_R(prod_{pleq M-1}p)pmod R \ displaystyle g_R(prod_{pleq M}p)equiv g_R(prod_{pleq M-1}p)pmod R end{cases}, M otin Prime \ \ egin{cases} displaystyle f_R(prod_{pleq M}p)equiv f_R(prod_{pleq M-1}p)cdot M^{[M eq R]}pmod R \ displaystyle g_R(prod_{pleq M}p)equiv g_R(prod_{pleq M-1}p)+[M=R]pmod R end{cases}, Min Prime end{cases})

    复杂度为 (O(N))

    对于 (displaystyle prod_{pleq M}(p-1))(egin{cases} egin{cases} displaystyle f_R(prod_{pleq M}(p-1))equiv f_R(prod_{pleq M-1}(p-1))pmod R \ displaystyle g_R(prod_{pleq M}(p-1))equiv g_R(prod_{pleq M-1}(p-1))pmod R end{cases}, M otin Prime \ \ egin{cases} displaystyle f_R(prod_{pleq M}(p-1))equiv f_R(prod_{pleq M-1}(p-1))cdot f_R(M-1)pmod R \ displaystyle g_R(prod_{pleq M}(p-1))equiv g_R(prod_{pleq M-1}(p-1))+g_R(M-1)pmod R end{cases}, Min Prime end{cases})

    对于 (f_R(M-1))(g_R(M-1)) 同样是暴力拆解,复杂度不大于 (f_R(N!))(g_R(N!)) 的求解,故也是 (O(N))

    因此预处理总复杂度为 (O(N)+O(N)+O(N)=O(N))


    【代码】

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int MAXN=1e7+10;
    int R, prime[MAXN], cntprime, frac[MAXN], cntR[MAXN], prodP[MAXN], cntRP[MAXN], prodP1[MAXN], cntRP1[MAXN];
    bool nprime[MAXN];
    inline ll fpow(ll a,ll x) { ll ans=1; for(;x;x>>=1,a=a*a%R) if(x&1) ans=ans*a%R; return ans; }
    inline int ans(int N, int M){
        if( cntR[N]+cntRP1[M]-cntRP[M] ) return 0;
        return (ll)frac[N]*prodP1[M]%R*fpow(prodP[M], R-2)%R;
    }
    inline void pre(){
        frac[0]=frac[1]=1;
        prodP[1]=prodP1[1]=1;
        for(int i=2;i<=1e7;++i){
            if(!nprime[i]) prime[++cntprime]=i;
            for(int j=1;j<=cntprime;++j)
                if((ll)prime[j]*i>1e7) break;
                else{
                    nprime[prime[j]*i]=1;
                    if(i%prime[j]==0) break;
                }
            cntR[i]=cntR[i-1];
            int Val=i;
            while(Val%R==0) ++cntR[i], Val/=R;
            frac[i]=(ll)frac[i-1]*Val%R;
    
            prodP[i]=prodP[i-1];
            cntRP[i]=cntRP[i-1];
            prodP1[i]=prodP1[i-1];
            cntRP1[i]=cntRP1[i-1];
            if(prime[cntprime]!=i) continue;
            if(i==R) ++cntRP[i];
            else prodP[i]=(ll)prodP[i]*i%R;
            Val=i-1;
            while(Val%R==0) ++cntRP1[i], Val/=R;
            prodP1[i]=(ll)prodP1[i]*Val%R;
        }
    }
    int main(){
        ios::sync_with_stdio(0);
        cin.tie(0); cout.tie(0);
        int T, N, M; cin>>T>>R;
        pre();
        while(T--&&cin>>N>>M) cout<<ans(N, M)<<"
    ";
        cout.flush();
        return 0;
    }
    
  • 相关阅读:
    Redis集群搭建&访问
    Redis集群功能概述
    Redis多机功能之Sentinel
    Redis单机版安装与部署
    Redis多机功能之复制
    Redis多机功能介绍
    Hadoop JobHistory
    Hive基础之COALESCE用法
    junit组合模式应用
    Emmet
  • 原文地址:https://www.cnblogs.com/JustinRochester/p/14798691.html
Copyright © 2020-2023  润新知