首先next_permutation打表,發現Cat規律。ios
其實考試的時候這麼作沒什麼問題,並且能夠節省異常多的時間,那麼如今咱們來想一下why。算法
首先我拿模型法解釋一下,咱們把2n個數當作2n我的,既然分紅奇數和偶數兩種比較方式,那麼我讓他們站成兩排,每一排有n我的,這n我的的身高遞增,且,第二排的人必須高於第一排,那麼這個問題就變成了:ide
有2n個身高互不相同人站成兩排,每排n人,要求右邊的人比左邊的人高,後面的人比前面的人高,問我有幾種排隊方案。函數
這是一個Cat的模型,既然先站哪一排無所謂,我就讓某個位置必須先站上第一排的人再站上第二排的人,若是我將站在第一排看作是0,站在第二排看作是1,那麼既然每一個1前面必定有一個比他矮的人,則必定有一個0,那麼就又轉化成了求0,1序列,這是一個更加經典的Cat模型。(若是這裏理解不了能夠上網搜搜)spa
而後再拿折線法解釋一下,咱們把偶數項看作x軸上的數,由於他們是單增的,把奇數項看作y軸上的數,因爲奇數項小於與之對應偶數項,也就是不能越過y=x,函數的變化就好像只能向右走和向上走。這個問題在上一篇博客中有詳細的解法。調試
因此咱們明白它是讓咱們求Cat,但是P不必定是質數,逆元的問題很噁心。code
因此咱們採用惟一分解來作。首先線性篩篩出2n之內的全部素數,而後咱們枚舉每一個素數,對n執行如下操做:將n不斷的除以這個素數,並將商加入s變量,最終s的值就是n!在算術基本定理拆分後,這個素數的指數。舉個例子:blog
8!=27*32*5*7,8/2=4,4/2=2,2/2=1,1/2=0。4+2+1+0=7。博客
20!=218……,20/2=10,10/2=5,5/2=2,2/2=1,1/2=0。10+5+2+1+0=18。string
你們能夠本身隨便試兩個。
這是爲何呢?(下述i爲質數)首先1~n中含有i這個因子的數有n/i個(1),含有i2這個因子的數有n/i2個(2),……含有im這個因子的數有n/im個(m)。那麼咱們分層計算貢獻,首先(1)中有n/i個i,加上,(2)中有2*n/i2個i,但不要忘了,咱們在(1)算過每一個數中的一個i,那麼它們的貢獻只有n/i2個i,同理,向後類推,最後n!中i的個數爲∑n/pi,與上述模擬過程一致。
那麼咱們來證實一下複雜度,首先根據小於N的質數約有N/lnN個,咱們第一層枚舉的代價就是O(N/lnN),而後觀察上述過程,咱們的問題規模不斷縮小,如上述二例,都是1/二、1/2的速度在縮小,對於其餘素數相似,咱們取最壞O(log2N),那麼總複雜度
O(N/lnN*log2N),這玩意換換底就是O(N/ln2),1/ln2≈1.44,撇掉,大約O(N),(這是我本身證的,網上目測沒有,若是有異議請指出,應該沒什麼問題吧……)
而後分子加分母減拆完了拿快速冪一乘就完事了。(快速冪並不影響上述複雜度,由於qpow也是O(logk)的,就當常數大了吧)。
(底下代碼有表機,勾掉的調試略多,能夠用來本身見證一下上面那個算法的正確性)
#include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<cstdio> #include<vector> #include<queue> #include<stack> #include<set> #include<map> using namespace std; int n,P,/*a[20],*/ans=1; /*bool check(){ for(int i=1;i<=n;i++) if(a[i*2-1]>a[i*2]) return 0; for(int i=3;i<=n*2;i++) if(a[i]<a[i-2]) return 0; return 1; }*/ int prime[6000000],prime_num; bool v[20050000]; void doprime(){ for(int i=2;i<=2*n+5;i++){ if(!v[i]) prime[++prime_num]=i; for(int j=1;j<=prime_num&&i*prime[j]<=2*n+5;j++){ v[prime[j]*i]=1; if(i%prime[j]==0) break; } } } int qpow(int x,int k){ int val=1; for(;k;k>>=1,x=1ll*x*x%P) if(k&1) val=1ll*val*x%P; return val%P; } int main(){ //打表找規律系列。。。 /* while(1){ ans=0; scanf("%d%d",&n,&P); for(int i=1;i<=(n<<1);i++) a[i]=i; do{ if(check()) {ans++; for(int i=1;i<=2*n;i++) cout<<a[i]<<" "; cout<<endl; } }while(next_permutation(a+1,a+1+2*n)); printf("ANS=%d\n",ans); }*/ scanf("%d%d",&n,&P); doprime(); for(int i=1;i<=prime_num;i++){ long long s=0; for(int j=2*n;j/=prime[i];) s+=j; // cout<<"s1="<<s<<endl; for(int j=n;j/=prime[i];) s-=j; //cout<<"s2="<<s<<endl; for(int j=n+1;j/=prime[i];) s-=j; // cout<<"s3="<<s<<endl; ans=1ll*ans*qpow(prime[i],s)%P; } // cout<<"Okprime"<<endl; /* for(int i=1;i<=prime_num;i++) cout<<prime[i]<<" ";cout<<endl;*/ /* for(int i=1;i<=2*n;i++) mulfz(i); for(int i=1;i<=n;i++) mulfm(i); for(int i=1;i<=n+1;i++) mulfm(i);*/ /* cout<<"OKfenjie"<<endl; for(int i=1;i<=prime_num;i++) ans=1ll*ans*qpow(prime[i],fz[i]-fm[i])%P; cout<<"Okqpow"<<endl;*/ printf("%d",ans); return 0; }
這道題取模,下道題高精。