題目大意
有一個長度爲的序列,其中元素爲的排列,其中第位的元素爲,問有多少種合法的序列是有規則的.
有規則的序列被定義爲,如果利用一個棧可以將這個序列排成升序,那麼這個序列就是有規則的.
solution
如果將入棧操作設爲’(’,將出棧操作設爲’)'的話,那麼序列可以變成一個長度爲的括號序列.
並且根據題意,第個左括號顯然會和第個右括號相配對.
那麼我們枚舉第個左括號所在的位置,設爲.
顯然的左邊有個左括號和個右括號,這是一個走格子的問題,可以用組合數解決.
那麼我們就可以根據這些信息算出與第個左括號匹配的右括號(也就是第個右括號)的位置.
而這兩個括號中間顯然就是長度爲的括號序,就是一個卡特蘭數.
有了這些信息,第個右括號右邊的括號數量就可以得出了,同樣是一個走格子問題,用組合數解決即可.
注意要判方案的合法性.
#include <cstdio>
#include <algorithm>
#include <cstring>
#define ll long long
using namespace std;
const int N = 2e6 + 5,mo = 1e9 + 7;
int n,pos,x;
ll ans;
ll inv[N],frac[N];
void prepare()
{
inv[0] = inv[1] = 1;
for (int i = 2 ; i < N ; i++) inv[i] = (mo - mo / i) * inv[mo % i] % mo;
for (int i = 2 ; i < N ; i++) inv[i] = inv[i - 1] * inv[i] % mo;
frac[0] = frac[1] = 1;
for (int i = 2 ; i < N ; i++) frac[i] = frac[i - 1] * i % mo;
}
ll C(int m,int n)
{
return frac[n] * 1LL * inv[m] % mo * inv[n - m] % mo;
}
ll solve(int n,int m)
{
if (n < 0 || m < 0) return 0;
return ((C(m,n + m) - C(m - 1,n + m)) % mo + mo) %mo;
}
int main()
{
freopen("stack.in","r",stdin);
freopen("stack.out","w",stdout);
scanf("%d%d%d",&n,&pos,&x);
prepare();
for (int i = pos ; i <= (pos << 1) - 1 ; i++)
{
int restl = n - pos - (x - i + pos - 1),restr = n - x;
ll s1 = solve(x - i + pos - 1,x - i + pos - 1),s2 = solve(pos - 1,i - pos),s3 = solve(restr,restl);
ans = (ans + s1 * s2 % mo * s3 % mo) % mo;
}
printf("%lld\n",ans);
return 0;
}