鍵盤上有左括號(,右括號),和退格鍵-,共三個鍵。
牛牛希望按鍵n次,使得輸入的字符串恰好一個合法的括號序列。
每按一次左括號(,字符串末尾追加一個左括號(
每按一次右括號),字符串末尾追加一個右括號)
每按一次退格鍵-,會刪掉字符串的最后一個字符,
特別的,如果字符串為空,牛牛也可以按退格,但是什么都不會發生。
輸出方案數對p取模,注意p可能不是質數。
注:只要按鍵方法不同,就是不同的方案,即使得到的序列一樣。
Solution
這題和其他關於括號序列的題不太一樣,因為有了刪除操作。
由於我太ZZ,然后搞了個*b方程。
設dp[i][j]表示按了i次,造出了一個長度為j的序列的方案數。
轉移是dp[i][j]=dp[i-1][max(0,j-1)]+dp[i-1][j+1]
這里取max是指當序列為空時我也可以退格。
然后我們就有了一個長度為j的序列,我們要把左括號和右括號往里放,使得它是一個合法的括號序列。
其實這個方案數就是卡特蘭數。
於是我們枚舉長度,答案加上dp[n][i]*catalan(i/2)。
然后樣例爆炸。
發現(+del和)+del最后制造出的序列是一樣的,但是是兩種不同的方案,但按照上面的方法dp只會計算一次。
所以刪除時要考慮刪什么字符。
正確轉移方程:dp[i][j]=dp[i-1][max(0,j-1)]+dp[i-1][j+1]*2.
Code
#include<iostream> #include<cstdio> #define N 1002 using namespace std; typedef long long ll; int n; ll f[N][N],dp[N][N],p,ans; int main() { scanf("%d%lld",&n,&p); dp[0][0]=1; for(int i=1;i<=n;++i) for(int j=0;j<=i;++j) dp[i][j]=(dp[i-1][max(j-1,0)]+2*dp[i-1][j+1])%p; f[0][0]=1; for(int i=1;i<=n;++i) { f[i][0]=f[i-1][1]; for(int j=1;j<=i;++j) f[i][j]=(f[i-1][j+1]+f[i-1][j-1])%p; } for(int i=0;i<=n;i+=2)(ans+=(f[i][0]*dp[n][i])%p)%=p; printf("%lld\n",ans); return 0; }