洛谷P2606 [ZJOI2010]排列計數
阿新 • • 發佈:2017-10-13
不知道 計算 color pan pac sin gis n) flag
題目描述
稱一個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是一個質數。
題目大意:求1--n能構成小根堆的排列
題解:
暴力30...
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; int n,p,ans,a[1000009]; int main(){ scanf("%d%d",&n,&p); for(int i=1;i<=n;i++)a[i]=i; do{ bool flag=false; for(register int i=2;i<=n;i++){ if(a[i]<a[i/2]){ flag=true;break; } } if(!flag)ans=(ans%p+1%p)%p; }while(next_permutation(a+1,a+n+1)); printf("%d\n",ans); return 0; }
正解:Lucas定理+樹形dp
沒看出來是小根堆...我這個沙茶...
然後根一定是最小的,然後f[i]=c(s[i]-1,s[i<<1])*f[l]*f[r]
f[i]表示以i為根的小根堆的數量....然後左子樹的大小就是從s[i]-1(減去根
中選出s[i<<1],用Lucas定理求就行啦...
因為有子問題的....
ps:不知道為什麽一直WA,抱著試試看的心態,我多加了一個取模。
你猜怎麽著?就A了....
#include<iostream> #include<cstdio> #include<cstring> #define maxn 1000009 #define LL long long using namespace std; LL n,p; LL f[maxn],inv[maxn],s[2*maxn],dp[maxn]; LL ksm(LL x,LL y){ LL ret=1%y; while(y){ if(y&1)ret=(1LL*ret*x)%p; x=1LL*x*x%p; y>>=1; } return ret; } void pre(){ f[0]=inv[0]=1; for(int i=1;i<=n;i++)f[i]=(1LL*f[i-1]*i)%p; for(int i=1;i<=n;i++)inv[i]=ksm(f[i],p-2)%p; } LL C(LL n,LL m){ return 1LL*f[n]*inv[m]%p*inv[n-m]%p; } LL Lucas(LL n,LL m){ if(!m)return 1; return C(n%p,m%p)*Lucas(n/p,m/p)%p; } int main(){ scanf("%lld%lld",&n,&p); pre(); for(int i=n;i>=1;i--){ s[i]=s[i<<1]+s[i<<1|1]+1; dp[i]=Lucas(s[i]-1,s[i<<1])%p; if((i<<1)<=n)dp[i]=dp[i]*dp[i<<1]%p; if((i<<1|1)<=n)dp[i]=dp[i]*dp[i<<1|1]%p; dp[i]=dp[i]%p; } printf("%lld\n",dp[1]%p); return 0; }
洛谷P2606 [ZJOI2010]排列計數