1. 程式人生 > >[ZJOI2010]排列計數

[ZJOI2010]排列計數

i++ 輸入輸出格式 namespace 數據 logs col -m 包含 大於

題目描述

稱一個1,2,...,N的排列P1,P2...,Pn是Magic的,當且僅當2<=i<=N時,Pi>Pi/2. 計算1,2,...N的排列中有多少是Magic的,答案可能很大,只能輸出模P以後的值

輸入輸出格式

輸入格式:

輸入文件的第一行包含兩個整數 n和p,含義如上所述。

輸出格式:

輸出文件中僅包含一個整數,表示計算1,2,?, ???的排列中, Magic排列的個數模 p的值。

輸入輸出樣例

輸入樣例#1:
20 23 
輸出樣例#1:
16

說明

100%的數據中,1 ≤N ≤ 10^6, P≤ 10^9,p是一個質數。

畫圖發現樹的形狀是唯一的

且對於一個子樹的根,一定小於所有子樹節點

也就是說,對於一個根節點,只要考慮給左右子樹劃分的方案
可以列出dp方程:

f[i]=f[2*i]*f[2*i+1]*C(size[2*i],size[2*i+1]+size[2*i])

這題據說n會大於p,也就是說1~n會含有p

那麽就不能線性求逆元

統計出i!中p出現的次數num[i]和不算p的倍數的階乘fac[i]

算組合數時,如果num[y]-num[x]-num[y-x]不為0直接返回0

逆元直接把fac[]帶入拓展歐幾裏德,因為在算fac時排除了p,所以可行

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4
#include<cstring> 5 using namespace std; 6 typedef long long lol; 7 lol f[1000001],size[1000001],p,num[1000001],fac[1000001],n; 8 lol exgcd(lol a,lol b,lol &x,lol &y) 9 { 10 if (b==0) 11 { 12 x=1;y=0; 13 return a; 14 } 15 lol d=exgcd(b,a%b,x,y); 16 lol t=x;x=y;y=t-(a/b)*y; 17 return
d; 18 } 19 lol reverse(lol a) 20 { 21 lol x,y; 22 exgcd(a,p,x,y); 23 return (x%p+p)%p; 24 } 25 lol C(int x,int y) 26 { 27 lol ap=num[y],bp=num[x],cp=num[y-x]; 28 if (ap-bp-cp) return 0; 29 lol s=(fac[y]*reverse(fac[x])%p)*reverse(fac[y-x])%p; 30 return s; 31 } 32 void dfs_dp(int x) 33 { 34 f[x]=1; 35 size[x]=1; 36 if (2*x<=n) 37 dfs_dp(2*x); 38 if (2*x+1<=n) 39 dfs_dp(2*x+1); 40 if (2*x<=n) 41 if (2*x+1>n||size[2*x+1]==0) 42 { 43 f[x]=f[2*x]; 44 size[x]+=size[2*x]; 45 } 46 else 47 { 48 f[x]=((f[2*x]*f[2*x+1]%p)*C(size[2*x],size[2*x]+size[2*x+1])%p)%p; 49 size[x]+=size[2*x]+size[2*x+1]; 50 } 51 } 52 int main() 53 {int i; 54 cin>>n>>p; 55 fac[0]=1; 56 for (i=1;i<=n;i++) 57 { 58 int x=i; 59 num[i]=num[i-1]; 60 while (x%p==0) 61 { 62 num[i]++; 63 x/=p; 64 } 65 if (i%p==0) fac[i]=fac[i-1]; 66 else fac[i]=fac[i-1]*i%p; 67 } 68 dfs_dp(1); 69 cout<<f[1]; 70 }

[ZJOI2010]排列計數