P10956 金字塔
虽然我还是看题解才会做的(菜),但我还要说这题挺简单的(菜),还是菜就多练。
因为是对一个字符串操作求树的可能数,所以考虑区间dp,设状态 \(dp[i][j]\) 为区间 \(i\) 到 \(j\) 树的状态数。
在转移的过程中如果 \(s_i=s_j\) 时代表这可能为同一个根节点组成的树,所以才可以开始枚举中间点,当中间点与根节点的颜色相同时,我们就可以将分为两个子树,然后根据乘法原理,将每个区间状态数相乘就是这么划分的方案数,再将所有的状态数相加就得到了大区间的状态数,转移方程为 \(dp[i][j]=(dp[i][j]+dp[i][k]\times dp[k+1][j-1])\),\(k+1\) 和 \(j-1\) 是为了得到两个不同子树乘积。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int mod=1e9;
const int N=1e5;
char s[N];
int n;
ll dp[1005][1005];
int main(){
ios::sync_with_stdio(false);
cin>>s+1;
n=strlen(s+1);
for(int i=1;i<=n;i++){
dp[i][i]=1;//初始化,只有1种可能
}
for(int len=2;len<=n;len++){//区间长度
for(int i=1;i<=n-len+1;i++){//枚举起点
int j=i+len-1;
if(s[i]==s[j]){//可能合法
for(int k=i;k<=j-1;k++){//枚举中间点
if(s[k]==s[i]){//得到两个子树
dp[i][j]+=(dp[i][k]*dp[k+1][j-1])%mod;
dp[i][j]%=mod;
}
}
}
}
}
cout<<dp[1][n];
return 0;
}