解题报告 洛谷P2155 [SDOI2008] 沙拉公主的困惑

P2155 [SDOI2008] 沙拉公主的困惑

题目
题面非常的简洁,求 \(\sum\limits_{i=1}^{n!}[i\perp m!]\)
直接颓式子,

\[\begin{aligned} ans&=\dfrac{n!}{m!}\cdot\varphi(m!)\\\\ &=\dfrac{n!}{m!}*m!\prod\limits_{p\mid m!}[\dfrac{p-1}{p}]\\ &=n!\cdot\dfrac{\prod\limits_{p\in\mathbb{P},p\le m}(p-1)}{\prod\limits_{p\in\mathbb{P},p\le m}p} \end{aligned} \]

p为1到m中的所有质数,逆元和阶乘预处理直接乘起来就行。
小粉兔提出模数可能会小于n,
\(mod\le m\le n\) 时,逆元遇到 \(p\mid i\) 时,\(inv_i=1\) ,然后阶乘时要判断,因为分子和分母可以消去 p 。
而当 \(m<mod\le n\) 时,即不能消去 p 时就直接输出0 。
感觉这道题有点毒瘤。
代码如下

#include<bits/stdc++.h>
#define int long long
using namespace std;
int prime[(int)(1e6)+5],
ny[(int)(1e7)+5],num[(int)(1e7)+5],
x,y,cj[(int)(1e6)+5];
int jie[(int)(1e7)+5];
bool vis[(int)(1e7)+5];
void getprime(int n){
    int m=0;
    for(int i=2;i<=n;i++){
        if(!vis[i])prime[++m]=i;num[i]=m;
        for(int j=1;j<=m&&prime[j]*i<=n;++j){
            vis[prime[j]*i]=1;
            if(i%prime[j]==0)break;
        }
    }
}
int read(){
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<1)+(x<<3)+(ch^48);
    return x*f;
}
void write(int x){
    if(x<0)putchar('-'),x=-x;
    if(x>9)write(x/10);
    putchar(x%10+48);
}
main(){
    int t,p,n,m;t=read();p=read();ny[0]=ny[1]=1;getprime((int)(1e7)+2);
    for(int i=2;i<=1e7;++i){ny[i]=(p-p/i)*ny[p%i]%p;}
    jie[1]=1;cj[0]=1;
    for(int i=2;i<=(int)(1e7)+1;++i)
        if(i==p)
            jie[i]=jie[i-1];
        else
            {jie[i]=i*jie[i-1]%p;}
    for(int i=1;i<=num[(int)1e7];++i){cj[i]=(prime[i]-1)*cj[i-1]%p*ny[prime[i]%p]%p;}
    while(t--){
        n=read(),m=read();
        if(n>=p&&m<p)
        {
            puts("0");
            continue; 
        }
        n=jie[n];int pre=num[m];
        write(n*cj[pre]%p);
        putchar('\n');
    }
    return 0;
}
/*
1 3
4 3
*/

另外可以优化,只处理 \(max(n)\) 即可。最优解就是这么卡的。

posted @ 2023-09-29 15:34  Ishar-zdl  阅读(66)  评论(1)    收藏  举报