洛谷P2606 [ZJOI2010]排列计数
Posted 小时のblog
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷P2606 [ZJOI2010]排列计数相关的知识,希望对你有一定的参考价值。
题目描述
称一个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]排列计数的主要内容,如果未能解决你的问题,请参考以下文章