Luogu P10956 金字塔
Solution
考虑区间 dp。很容易想到定义 \(dp_{l,r}\) 表示区间 \([l,r]\) 对应的满足条件的子树的方案数。
一般区间 dp 的套路无非就是枚举一个断点 \(k\),使得这个大状态由两个小状态转移过来,我们现在需要考虑的就是如何划分每一个状态。
状态对应的子树也有若干个子树。不妨只考虑第一棵子树是什么样的,这样我们将整个状态划分成了第一棵子树和其他子树。所以考虑递归地把每一个 \(dp_{l,r}\) 计算。具体地,每次依然枚举一个断点 \(k\),分割第一棵子树和其他子树,再分别递归计算第一棵子树和其他子树的答案。
下面假设每一个 \(dp_{l,r}\) 已经算出,那么:
\[dp_{l,r}=\sum^{r}_{k=l+2}dp_{l+1,k-1} \times dp_{k,r}
\]
Code
使用的是记忆化搜索。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9;
string str;
int n,dp[305][305];
int dfs(int l,int r){
if(l>r) return 0;
if(l==r) return 1;
if(str[l]!=str[r]) return 0;
if(dp[l][r]!=-1) return dp[l][r];
dp[l][r] = 0;
for(int i=l+2;i<=r;i++){
if(str[l]==str[i]) dp[l][r]=(dp[l][r]+dfs(l+1,i-1)*dfs(i,r))%mod;
}
return dp[l][r];
}
signed main(){
cin>>str;
n=str.size();
str=" "+str;
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j++){
dp[i][j]=-1;
}
}
cout<<dfs(1,n)<<endl;
return 0;
}