1. 程式人生 > >【學術篇】SDOI2008 沙拉公主的困惑

【學術篇】SDOI2008 沙拉公主的困惑

pro com blog logs 百度 () printf mark 是我

傳送門!

題目在這裏...

題目大意?

難道不是說的很清楚了麽OvO
求n!中與m!互質的數的個數..

題目分析.

顯然的數論... 所以就是化式子唄..
一個很顯然的性質就是如果\(gcd(a,b)=1\),那麽\(gcd(a+kb,b)=1\)...
而題目中說了\(m\leqslant n\), ∴ \(m!|n!\)
於是我們只需要計算\(m!\)中與\(m!\)互質的數的個數,然後乘以\(\frac{n!}{m!}\)即可..
我們發現上面加粗的這一坨就是\(\varphi(m!)\)嘛...
所以\(ans=\varphi(m!)*\frac{n!}{m!}\)
又有\(\varphi(x)=x*\prod_{i}^{n}(1-\frac{1}{p_i})\)

其中\(p_i\)表示x的質因數...
\(m!=1*2*...*m\), 所以\(m!\)的質因數很顯然就是不大於\(m\)的質數...
然後帶入上式約掉\(m!\)就有了\(ans=n!*\prod_{i}^{n}\frac{p_i-1}{p_i}\) (其中\(p_i\leqslant m\)\(p_i\)為質數)...
由於多組詢問, 而且內存開了256MB不是 所以我們要預處理... 不然會T...
由於上式, 我們要預處理的東西有:

  • 篩素數(簡單歐拉篩)
  • 階乘(順著乘一遍取模就行了)
  • 逆元(要遞推求出所有數的哦) (所以最好用\(O(n)\)的, 不會的話直接看代碼就行了 百度一下一堆詳細講解OvO)
  • \(mul_i=\prod_{i}^{n}\frac{p_i-1}{p_i}\)這一坨東西...(不大於\(m\)的質數\(p_i\)們的\((1-\frac{1}{p_i})\)的乘積...)
    然後處理這一坨的時候也很容易...遞推即可.. 顯然, 我們有
  1. \(i\)是質數時, \(mul_i=mul_{i-1}*\frac{i-1}{i}\)
  2. 否則\(mul_i=mul_{i-1}\)即可...
    這樣就做完了.

實現代碼:

#include <cstdio>
typedef long long LL;
const int X=1e7+3;
inline int gn(int
a=0,char c=0){ for(;c<48||c>57;c=getchar()); for(;c>47&&c<58;c=getchar()) a=a*10+c-48; return a; } int inv[X],fac[X],eu[X],mul[X],pri[X/10],tot; bool notp[X]; int T,R,M,N; void prime(){ notp[1]=1; for(int i=2;i<X;++i){ if(!notp[i])pri[++tot]=i; for(int j=1;j<=tot&&i*pri[j]<=1e7;++j){ notp[i*pri[j]]=1; if(i%pri[j]==0) break; } } } void calcinv(){ inv[1]=1; for(int i=2;i<X;++i){ inv[i]=(LL)(R-R/i)*inv[R%i]%R; if(inv[i]<0) inv[i]+=R; } } void calcfac(){ fac[1]=1; for(int i=2;i<X;++i) fac[i]=(LL)fac[i-1]*i%R; } void calcmul(){ mul[1]=1; for(int i=2;i<X;++i) if(!notp[i]) mul[i]=(LL)mul[i-1]*(i-1)%R*inv[i]%R; else mul[i]=mul[i-1]; } int main(){ T=gn(),R=gn(); prime(); calcinv(); calcfac(); calcmul(); while(T--){ N=gn(),M=gn(); printf("%d\n",(int)((LL)fac[N]*mul[M]%R)); } }

註意事項~

  1. 做乘法的時候要轉long long,(當然你要是全用long long算當我沒說
  2. 預處理的時候1的值作為邊界值給出, 循環要從2開始
  3. 每一步都記得取模
  4. 輸出的時候記得換行而不是空格(我是不是暴露了什麽←_←

完結撒花

【學術篇】SDOI2008 沙拉公主的困惑