题目链接
https://www.acwing.com/problem/content/286/
题意
给出树的欧拉序,树的子树结点有序,问树的可能形态数
思路
显然区间DP。
我们让dp[l][r]为l-r的方案数,可以确定初值为dp[i][i]=1, 答案为dp[1][n]。考虑状态转移。我们不能枚举分界点个数与分界处,这样复杂度就起飞了。假设我们要枚举一个分界点,两个区间分别对应多个子树,那么可能出现重复。
为了避免重复,我们可以考虑第一个子树的分界点k。比如l—r是这段区间,那么第一个子树我们令其为l+1—k-1,剩下的子树是k—r。这两个dp相乘即可。这里注意隐藏条件,s[l]=s[k]=s[r]时才可以计算,l是进入这段区间的入口,r是出口,k是中转点也是入口出口那个点。因此他们必须相同。
代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
#define int long long
//#define double long double
using namespace std;
typedef long long ll;
const int maxn=505;
const int inf=0x3f3f3f3f;
const int mod=1e9;
int n,m,k;
int a[maxn];
int ans;
int dp[maxn][maxn];
inline int pls(int a,int b){
return (a+b)%mod;
}
inline int mul(int a,int b){
return (a*b)%mod;
}
signed main(){
IOS
#ifndef ONLINE_JUDGE
freopen("IO\\in.txt","r",stdin);
freopen("IO\\out.txt","w",stdout);
#endif
int tn=1;
string s;
cin>>s;
memset(dp,0,sizeof dp);
n=s.size();
for(int i=1;i<=n;i++)
dp[i][i]=1;
s='$'+s;
for(int len=3;len<=n;len++){
for(int l=1;l+len-1<=n;l++){
int r=l+len-1;
if(s[l]!=s[r]){
dp[l][r]=0;
continue;
}
int &t=dp[l][r];
for(int k=l+2;k<=r;k++){
if(s[l]==s[k])
t=pls(t,mul(dp[l+1][k-1],dp[k][r]));
}
}
}
cout<<dp[1][n]%mod<<endl;
}