题解 luoguP2155 【[SDOI2008]沙拉公主的困惑】

这题真喵喵的毒瘤,写的时候被时空双卡。

为了发泄愤怒,我来水一篇题解

题意:求 1 n ! 1-n! 1n! 中与 m ! m! m! 互质的数的个数 ( m &lt; = n ) (m&lt;=n) (m<=n)

我们发现小于 m ! m! m! 的数中,与 m ! m! m! 互质的数有 φ ( m ! ) φ(m!) φ(m!)个,那如何求大于 m ! m! m! 的数中与 m ! m! m! 互质的数的个数呢?

引理:若 a , b a,b a,b互质,则 a + b × k a+b\times k a+b×k b b b互质。

证明:当 g c d ( a , b ) = 1 gcd(a,b)=1 gcd(a,b)=1时, g c d ( b , a % b ) = 1 gcd(b,a\% b)=1 gcd(b,a%b)=1

a % b = a b × k \because a\% b=a-b\times k a%b=ab×k

g c d ( b , a b × k ) = 1 \therefore gcd(b,a-b\times k)=1 gcd(b,ab×k)=1

得证。

那么对于一个小于 m ! m! m! 且与 m ! m! m! 互质的数 x x x x + 1 × m ! x+1\times m! x+1×m! x + 2 × m ! x+2\times m! x+2×m! …都与 m ! m! m! 互质。

因为 n &gt; = m n&gt;=m n>=m,所以 n ! n! n! m ! m! m!的倍数,那么 1 n ! 1-n! 1n! 中与 m ! m! m! 互质的数的个数,根据上面的结论,很显然就是:

r e s = φ ( m ! ) × n ! m ! = m ! × <munderover> i = 1 k </munderover> ( p i 1 p i ) × n ! m ! = n ! × <munderover> i = 1 k </munderover> ( p i 1 p i ) res=φ(m!)\times \frac{n!}{m!}=m!\times \prod\limits_{i=1}^{k}\left (\dfrac{p_i-1}{p_i}\right)\times \frac{n!}{m!}=n!\times \prod\limits_{i=1}^{k}\left (\dfrac{p_i-1}{p_i}\right) res=φ(m!)×m!n!=m!×i=1k(pipi1)×m!n!=n!×i=1k(pipi1)

我们用 a n s ans ans数组记录最后的式子的右边的东西, a n s [ i ] ans[i] ans[i]表示 k k k i i i时右边东西的值。 j c [ i ] jc[i] jc[i]表示 i ! i! i! n y [ i ] ny[i] ny[i]表示 i i i的逆元。

根据公式,很容易就能写出,答案 = j c [ n ] × a n s [ m ] =jc[n]\times ans[m] =jc[n]×ans[m]。预处理出三个数组,就能做到 O ( 1 ) O(1) O(1)查询,这题就做完了,撒花~。

空间有点紧,不能都开 l o n g <mtext>   </mtext> l o n g long \ long long long,不该开的就不用开了。

#include<bits/stdc++.h>
#define ts cout<<"ok"<<endl
#define oo (1e18)
#define ll long long
#define LL unsigned long long
#define hh puts("")
using namespace std;
int T,top,n,m,ny[10000005],ans[10000005],pr[6000005];
bool v[10000005];
ll r,jc[10000005];
inline int read(){
    int ret=0,ff=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-') ff=-1;ch=getchar();}
    while(isdigit(ch)){ret=(ret<<3)+(ret<<1)+(ch^48);ch=getchar();}
    return ret*ff;
}
signed main(){
    scanf("%d%lld",&T,&r);
    jc[0]=jc[1]=1;
    ny[0]=ny[1]=1;
    ans[0]=ans[1]=1;
    for(int i=2;i<=10000000;i++){
        jc[i]=jc[i-1]*(ll)i%r;
        ny[i]=(ll)(r-r/i)*ny[r%i]%r;
        if(!v[i]) pr[++top]=i;
        for(int j=1;j<=top&&i*pr[j]<=10000000;j++){
            v[i*pr[j]]=1;
            if(i%pr[j]==0) break;
        }
    }
    for(int i=2;i<=10000000;i++){
        ans[i]=ans[i-1];
        if(!v[i]) ans[i]=(ll)ans[i]%r*(i-1)%r*ny[i]%r;
    }
    while(T--){
        n=read(),m=read();
        ll res=(ll)ans[m]%r*jc[n]%r;
        printf("%lld\n",res);
    }
    return 0;
}
全部评论

相关推荐

最近群里有很多同学找我看简历,问问题,主要就是集中在明年三月份的暑期,我暑期还能进大厂嘛?我接下来该怎么做?对于我来说,我对于双非找实习的一个暴论就是title永远大于业务,你在大厂随随便便做点慢SQL治理加个索引,可能就能影响几千人,在小厂你从零到一搭建的系统可能只有几十个人在使用,量级是不一样的。对双非来说,最难的就是约面,怎么才能被大厂约面试?首先这需要一点运气,另外你也需要好的实习带给你的背书。有很多双非的同学在一些外包小厂待了四五个月,这样的产出有什么用呢?工厂的可视化大屏业务很广泛?产出无疑是重要的,但是得当你的实习公司到了一定的档次之后,比如你想走后端,那么中厂后端和大厂测开的选择,你可以选择中厂后端(注意,这里的中厂也得是一些人都知道的,比如哈啰,得物,b站之类,不是说人数超过500就叫中厂),只有这个时候你再去好好关注你的产出,要不就无脑大厂就完了。很多双非同学的误区就在这里,找到一份实习之后,就认为自己达到了阶段性的任务,根本不再投递简历,也不再提升自己,玩了几个月之后,美其名曰沉淀产出,真正的好产出能有多少呢?而实际上双非同学的第一份实习大部分都是工厂外包和政府外包!根本无产出可写😡😡😡!到了最后才发现晚了,所以对双非同学来说,不要放过任何一个从小到中,从中到大的机会,你得先有好的平台与title之后再考虑你的产出!因为那样你才将将能过了HR初筛!我认识一个双非同学,从浪潮到海康,每一段都呆不久,因为他在不断的投递和提升自己,最后去了美团,这才是双非应该做的,而我相信大部分的双非同学,在找到浪潮的那一刻就再也不会看八股,写算法,也不会打开ssob了,这才是你跟别人的差距。
迷茫的大四🐶:我也这样认为,title永远第一,只有名气大,才有人愿意了解你的简历
双非本科求职如何逆袭
点赞 评论 收藏
分享
牛至超人:哈工大已经很棒了,不需要加括号了,然后咋没有实习经历呢?火速趁寒假整一段实习,导师不让就狠狠肘击
投了多少份简历才上岸
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务