BZOJ 1485 [HNOI2009]有趣的數列
阿新 • • 發佈:2019-02-19
首先將2n個數排列為序列A,從前向後選出n個作為奇數項,剩下的作為偶數項,而且選定的陣列成的有趣的數列只能有一種(選出的奇數項數字和偶數項數字要升序插入數列,排列只有一種)。要保證奇數項小於偶數項,那麼對於每一位A中的數,其前選出的奇數項的個數必然要大於等於偶數項的個數。把它轉化成了經典的進出棧問題。
對於進出棧問題也複習一下:設n個數進出站的總數為h(n),設最後時刻出站的元素為i,那麼比i先進棧且先出棧的種類數為:h(i-1),比i後進棧且先出棧的種數為:h(n-i+1),而i的取值範圍為1~n,於是有h(n)= h(0)*h(n-1)+h(1)*h(n-2) + … + h(n-1)h(0)
首先打了個遞推式的寫法,複雜度
然後直接用卡特蘭數的組合數公式,分解一下質因數會達到O(nlogn)。
(50分)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
int n,mod;
int dp[1000005];
int main()
{
scanf("%d%d",&n,&mod);
dp[0 ]=dp[1]=1;
for(int i=2;i<=n;i++)
{
for(int j=0;j<=i-1;j++)
dp[i]=(dp[i]+1LL*dp[j]%mod*dp[i-j-1]%mod)%mod;
}
cout<<dp[n];
return 0;
}
(100分)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int maxn=2000005;
int n,mod,cnt;
int prime[maxn],num[maxn],fa[maxn][2];
bool is[maxn];
void linear_shaker()
{
for(int i=2;i<maxn;i++)
{
if(!is[i])
{
prime[++cnt]=i;
fa[i][0]=i;
fa[i][1]=1;
}
int t;
for(int j=1;j<=cnt&&(t=prime[j]*i)<maxn;j++)
{
is[t]=true;
fa[t][0]=prime[j];
fa[t][1]=i;
if(i%prime[j]==0)break;
}
}
}
void update(int x,int val)
{
while(is[x])
{
num[fa[x][0]]+=val;
x=fa[x][1];
}
num[x]+=val;
}
int _pow(int x,int y)
{
int res=1;
while(y)
{
if(y&1)res=(1LL*res*x)%mod;
x=(1LL*x*x)%mod;
y>>=1;
}
return res;
}
int cal()
{
int res=1;
for(int i=2;i<maxn;i++)
if(num[i])res=(1LL*res*_pow(i,num[i]))%mod;
return res;
}
int main()
{
scanf("%d%d",&n,&mod);
linear_shaker();
for(int i=n+1;i<=2*n;i++)
update(i,1);
for(int i=2;i<=n;i++)
update(i,-1);
update(n+1,-1);
printf("%d",cal());
return 0;
}