【BZOJ】1485: [HNOI2009]有趣的数列
【算法】Catalan数
【题解】
学了卡特兰数就会啦>_<!
因为奇偶各自递增,所以确定了奇偶各自的数字后排列唯一。
那么就是给2n个数分奇偶了,是不是有点像入栈出栈序呢。
将做偶数标为-1,做奇数标为+1,显然当偶数多于奇数时不合法,因为它压不住后面的奇数。
然后其实这种题目,打表就可知啦……QAQ
然后问题就是求1/(n+1)*C(2n,n)%p了,p不一定是素数。
参考bzoj礼物的解法。
看到网上清一色的素数筛+分解质因数解法,思考了好久,感觉写了假的礼物……
后来试了一下发现礼物的做法慢得多,原因应该是礼物解法复杂度O(min(n,P))而且常数大,分解质因数O(n),但我觉得也带常数呀?
很奇怪……不过反正n太大只能用礼物的做法,不大的话分解质因数应该更快。
#include<cstdio> #include<cstring> #include<cctype> #include<cmath> #include<algorithm> #define ll long long using namespace std; /*------------------------------------------------------------*/ const int inf=0x3f3f3f3f,maxn=3000010; ll n,P,m,p[200],pc[200],M[200],a[200]; ll num,fac[maxn]; ll power(ll x,ll k,ll p) { ll ans=1; while(k>0) { if(k&1)ans=ans*x%p; x=x*x%p; k>>=1; } return ans; } void exgcd(ll a,ll b,ll &x,ll &y) { if(!b){x=1;y=0;return;} else {exgcd(b,a%b,y,x);y-=x*(a/b);} } ll inv(ll x,ll p) { ll xx,yy; exgcd(x,p,xx,yy); return ((xx%p)+p)%p; } ll calc(ll x,ll p,ll pc) { if(x<p)return fac[x]; num+=x/p; return fac[x%pc]*power(fac[pc-1],x/pc,pc)%pc*calc(x/p,p,pc)%pc; } ll work(ll p,ll pc) { fac[0]=1; for(ll i=1;i<min(pc,2*n+10);i++)fac[i]=fac[i-1]*(i%p==0?1:i)%pc; num=0; ll n1=calc(2*n,p,pc); ll tmp=num; for(ll i=1;i<min(pc,2*n+10);i++)fac[i]=fac[i-1]*(i%p==0?1:inv(i,pc))%pc; num=0; ll n2=calc(n,p,pc)*calc(n,p,pc)%pc; ll np=n+1; while(np%p==0){num++;np/=p;} n2=n2*inv(np,pc)%pc; return n1*n2*power(p,tmp-num,pc)%pc; } int main() { scanf("%lld%lld",&n,&P); m=0;ll nowP=P; for(ll i=2;i*i<=nowP&&nowP>1;i++) { if(nowP%i==0){p[++m]=i;pc[m]=1;} while(nowP%i==0){pc[m]*=i;nowP/=i;} } if(nowP>1){p[++m]=nowP;pc[m]=nowP;} ll ans=0; for(ll i=1;i<=m;i++) { M[i]=P/pc[i]; a[i]=work(p[i],pc[i]); ans=(ans+a[i]*M[i]%P*inv(M[i],pc[i])%P)%P; } printf("%lld",(ans%P+P)%P);//答案一定要记得取非负 return 0; }