1. 程式人生 > >[bzoj1485][HNOI2009]有趣的數列_卡特蘭數_組合數

[bzoj1485][HNOI2009]有趣的數列_卡特蘭數_組合數

有趣的數列 bzoj-1485 HNOI-2009

題目大意:求所有1~2n的排列滿足奇數項遞增,偶數項遞增。相鄰奇數項大於偶數項的序列個數%P。

註釋:$1\le n\le 10^6$,$1\le P \le 10^9$。


想法:好題啊。

我們依次考慮1~2n,就是把當前$i$放進奇數項還是偶數項的問題。因為我們有相鄰奇數項大於偶數項的問題。所以當前放進奇數項的個數不能多於放進偶數項的個數。

進而我們將放進奇數項比作進棧,放進偶數項比作出棧。

答案就相當於$n$的出棧入棧序的個數。

等於$Catalan_n$。

利用卡特蘭數的通項公式:$Catalan_n=\frac{C_{2n}^{n}}{(n+1)}$。

$=\frac{(2n)!}{n!(n+1)!}$。

用列舉質因子的方式求每個質因子的貢獻即可。

Code:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
ll mod;
bool vis[2000010];
int prime[2000010],cnt;
ll qmul(ll x,ll y)
{
	ll ans=0; x%=mod,y%=mod; while(y)
	{
		if(y&1) (ans+=x)%=mod;
		y>>=1;
		(x+=x)%=mod;
	}
	return ans;
}
ll qpow(ll x,ll y)
{
	ll ans=1; x%=mod; while(y)
	{
		if(y&1) (ans*=x)%=mod;
		y>>=1;
		(x*=x)%=mod;
	}
	return ans;
}
void init()
{
	for(int i=2;i<=2000000;i++)
	{
		if(!vis[i]) prime[++cnt]=i;
		for(int j=1;j<=cnt&&1ll*i*prime[j]<=2000000;j++)
		{
			vis[i*prime[j]]=true;
			if(i%prime[j]==0) break;
		}
	}
}
ll num(ll x,ll p)
{
	ll re=0; while(x)
	{
		re+=(x/p); x/=p;
	}
	return re;
}
int main()
{
	init();
	ll n; cin >> n >> mod ;
	ll ans=1;
	for(int i=1;i<=cnt&&prime[i]<=n*2;i++)
	{
		ans=qmul(ans,qpow(prime[i],num(2*n,prime[i])-num(n,prime[i])-num(n+1,prime[i])));
	}
	cout << ans << endl ;
	return 0;
}

小結:好題啊。關於模型的轉化總是非常重要且巧妙的。