题目来源:
http://acm.hrbust.edu.cn/contests/index.php?act=showproblem&cid=1366&p=C
Description |
Quasrain 和 FZ 是好朋友。Quasrain 善于挖坑而 FZ 善于填坑。 这个游戏一共会持续 2N 天。在每一天都会有人挖坑或者填坑,并在本子上记录下“A”表示这天挖了坑,“B”表示填了坑。填坑必须填一个现存的还没有被填过的坑。 作为素质优秀的熊孩子,他们保证在最后一天结束的时候一定会恰好填平所有坑。 问本子上可能有多少种不同的 AB 序列 |
Input |
第一行一个数 T 表示数据组数(T<=100000) 之后 T 行每行一个数 N,意义如题面所示(1<=N<=1000)
|
Output |
对于每组数据输出方案数,对 998244353 取模 |
Sample Input |
3 1 2 3 |
Sample Output |
1 2 5 |
Hint |
对于第三组样例: 可能的 AB 序列分别为:{AAABBB},{AABABB},{AABBAB},{ABAABB},{ABABAB} |
题目意思就是求在后面的填坑数要大于前面的挖坑次序可能情况,我们可以把填坑看成入栈操作,挖坑看成出栈操作,即填坑的累计个数不小于挖坑的排列有多少种,标准的卡特兰数列;
什么是卡特兰数列:
特兰数是一个常用在计数情况中使用的一种特殊的数列,其分析如下:
分析:假设我们要求的出栈数为n,要得到的出栈序列为f(n),我们知道,因为入栈的顺序是确定的,假设入栈顺序记为1、2、3、4、5...n,那么假设最后出栈的那个数为第k个数,那么我们要求f(k)时,k-1个数已经先完成进栈出栈,此时有f(k-1)种方式,然后k之后的n-k个数也完成进栈和出栈,也就是f(n-k)种方式,最后第k个数出栈,此时的f(k)=f(k-1)*f(n-k),而每个数都可能是最后出栈,可以得到通式:
其递推公式的通解(f(0)=1):
1:f(n)=C(2n,n)/n+1
2:f(n)=C(2n,n)-C(2n,n+1)))
3:f(n)=f(n-1)(4n-2)/(i+1);
坑点:由于我只知道第一个和第三个式子,存在除法取余,比赛的时候不会写逆元
另外记录一些取模公式:
-
a + b) % mod = (a % mod + b % mod) % mod (1)
-
(a - b) % mod= (a % mod - b % mod) % mod (2)
-
(a * b) % mod = (a % mod * b % mod) % mod(3)
-
a ^ b % mod = ((a % mod)^b) % mod (4)
-
结合律:
((a+b) % nod + c) % mod = (a + (b+c) % mod) % mod (5)
((a*b) % mod * c)% mod = (a * (b*c) % mod) % mod (6)
-
交换律:
(a + b) % mod = (b+a) % mod (7)
(a * b) % mod = (b * a) % mod (8)
- 分配律:
((a +b)% mod * c) % mod = ((a * c) % mod + (b * c) % mod) % mod (9)
费马定理小定理:a^b%mod=a^(b%(mod-1))%mod
a/b%mod=a*(1/b)%mod=a*b^(mod-2)%mod
(这里b^(mod-2)就是b的逆元)
参考代码:
#include<iostream>
using namespace std;
const int N=1e3;
const int mod=998244353;
typedef long long ll;
ll qpow(ll m,ll q)
{
ll ans=1;
while(q)
{
if(q&1)
ans=ans*m%mod;
m=m*m%mod;
q>>=1;
}
return ans;
}
ll inv[N+5];
ll getinv(ll n)
{
ll num3=qpow(n,mod-2);
return num3;
}
int main()
{
inv[1]=1,inv[2]=2;
for(ll i=3; i<=1005; i++)
{
ll num1=inv[i-1]*(4*i-2)%mod;
ll num2=getinv(i+1);
inv[i]=num1*num2%mod;
}
ll t,n;
cin>>t;
while(t--)
{
cin>>n;
cout<<inv[n]<<endl;;
}
return 0;
}
DP代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#define ll long long int
#define N 1005
#define mod 998244353
using namespace std;
ll dp[N][N];
int main()
{
// freopen("E:c++.txt","w",stdout);
memset(dp,0,sizeof(dp));
for(int i=0; i<=1000; i++)
dp[i][0]=1;//当挖坑的数量为零时,数量为1
for(int i=1; i<=1000; i++)
for(int j=1; j<=i; j++)
dp[i][j]=(dp[i][j-1]+dp[i-1][j])%mod;//dp[i][j]的方案数为填掉上一个的数量dp[i-1][j]和挖一个填一个的数量dp[i][j-1];
int t,n;
cin>>t;
while(t--)
{
cin>>n;
cout<<dp[n][n]<<endl;
}
return 0;
}
卡特兰数详细证明过程请参考:https://blog.csdn.net/nuoyanli/article/details/88928767