BZOJ2111:[ZJOI2010]排列计数——题解
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ2111:[ZJOI2010]排列计数——题解相关的知识,希望对你有一定的参考价值。
https://www.lydsy.com/JudgeOnline/problem.php?id=2111
https://www.luogu.org/problemnew/show/P2606#sub
称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Magic的,答案可能很大,只能输出模P以后的值
画成二叉树后容易发现这就是一个小根堆。
于是就变成了求符合条件的小根堆数量。
显然根只能放当前最小数,然后给左子树分配左子树大小个数,右子树同理。
所以就有f[i]=C(i-1,l)*f[l]*f[r]。
另外这题卡快速幂的log,所以预处理。
#include<cstdio> #include<iostream> #include<vector> #include<queue> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int N=1e6+5; inline int read(){ int X=0,w=0;char ch=0; while(!isdigit(ch)){w|=ch==\'-\';ch=getchar();} while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); return w?-X:X; } int lg[N],f[N],inv[N],fac[N]; int qpow(ll k,int n,int p){ int ans=1; while(n){ if(n&1)ans=(ll)ans*k%p; k=(ll)k*k%p;n>>=1; } return ans; } int C(int n,int m,int p){ if(m>n)return 0; if(m==n)return 1; return (ll)fac[n]*inv[m]%p*inv[n-m]%p; } int lucas(int n,int m,int p){ int ans=1; while(n&&m&&ans){ ans=(ll)ans*C(n%p,m%p,p)%p; n/=p,m/=p; } return ans; } inline int lsize(int n){ int c=lg[n]+1; if(c==1)return 0; int t=n-(1<<c-1)+1; return (1<<c-2)-1+min((1<<c-1>>1),t); } int main(){ int n=read(),p=read(); lg[1]=0;fac[1]=1; for(int i=2;i<=n;i++){ lg[i]=lg[i-1]; if((1<<lg[i]+1)==i)lg[i]++; fac[i]=(ll)fac[i-1]*i%p; } int mx=min(p-1,n); inv[mx]=qpow(fac[mx],p-2,p); for(int i=mx-1;i>=0;i--)inv[i]=(ll)inv[i+1]*(i+1)%p; f[1]=f[2]=1; for(int i=3;i<=n;i++){ int l=lsize(i); f[i]=(ll)lucas(i-1,l,p)*f[l]%p*f[i-l-1]%p; } printf("%d\\n",f[n]); return 0; }
+++++++++++++++++++++++++++++++++++++++++++
+本文作者:luyouqi233。 +
+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/ +
+++++++++++++++++++++++++++++++++++++++++++
以上是关于BZOJ2111:[ZJOI2010]排列计数——题解的主要内容,如果未能解决你的问题,请参考以下文章
bzoj 2111: [ZJOI2010]Perm 排列计数 (dp+卢卡斯定理)
bzoj 2111 [ZJOI2010]Perm 排列计数(DP+lucas定理)