金字塔
-
题目
-
分析
f [ l ] [ r ] f[l][r] f[l][r] 表示 子串 s [ l ∼ r ] s[l \sim r] s[l∼r] 对应着多少不同方案数。如果枚举子串 s [ l ∼ r ] s[l \sim r] s[l∼r] 划分点数量和所有划分点的位置,复杂度太高。
又可想到把子串分成两个部分,每部分可由若干颗子树组成,不过这样可能会产生重复计数。
所以可以只考虑子串 s [ l ∼ r ] s[l \sim r] s[l∼r] 的第一颗子树是由哪一段构成的。枚举划分点 k k k ,令子串 s [ l + 1 ∼ k − 1 ] s[l + 1 \sim k-1] s[l+1∼k−1] 构成
[ l , r ] [l,r] [l,r] 的第一颗子树, s [ k , r ] s[k,r] s[k,r] 构成 [ l , r ] [l,r] [l,r] 的剩余部分。
当这棵树只有一个子树时:
F [ l , r ] = F [ l + 1 , r − 1 ] F[l,r] = F[l + 1,r - 1] F[l,r]=F[l+1,r−1]
F [ l , r ] = { 0 s [ l ] ̸ = s [ r ] F [ l + 1 , r − 1 ] + ∑ l + 2 ≤ k ≤ r − 2 F [ l + 1 , k − 1 ] × F [ k , r ] s [ l ] = = s [ r ] F[l,r]=\left\{ \begin{aligned} 0 \qquad s[l] \not= s[r]\\ F[l + 1,r - 1]+\sum_{l+2\leq k \leq r-2} F[l+1,k-1] \times F[k,r] \qquad s[l] == s[r]\\ \end{aligned} \right. F[l,r]=⎩⎪⎨⎪⎧0s[l]̸=s[r]F[l+1,r−1]+l+2≤k≤r−2∑F[l+1,k−1]×F[k,r]s[l]==s[r]
初值: ∀ l ∈ [ 1 , N ] \forall l \in [1,N] ∀l∈[1,N] , f [ l , l ] = 1 f[l,l] = 1 f[l,l]=1 其余为 0 0 0 .目标: f [ 1 , N ] f[1,N] f[1,N]
当 k = r − 1 k = r - 1 k=r−1 F [ l , r ] = 0 F[l,r] = 0 F[l,r]=0
当 k = r k = r k=r F [ l , r ] = F [ l + 1 , k − 1 ] F[l,r] = F[l+1,k-1] F[l,r]=F[l+1,k−1]
所以可以转换为:
F [ l , r ] = ∑ l + 2 ≤ k ≤ r F [ l + 1 , k − 1 ] × F [ k , r ] F[l,r] = \sum_{l+2\leq k \leq r} F[l+1,k-1] \times F[k,r] F[l,r]=l+2≤k≤r∑F[l+1,k−1]×F[k,r] -
代码
int f[310][310]; char s[100000]; int solve(int l, int r) { if (l > r) return 0; if (l == r) return 1; if (f[l][r] != -1) return f[l][r]; if(s[l] != s[r]) return 0; f[l][r] = 0; for (int k = l + 2; k <= r; k++) f[l][r] = (f[l][r] + (ll)solve(l + 1, k - 1) * solve(k, r)) % mod; return f[l][r]; } int main () { //freopen("input.in", "r", stdin); //freopen("test.out", "w", stdout); scanf("%s",s+1); memset(f,-1,sizeof(f)); cout << solve(1,strlen(s+1)) << endl; return 0 ; }
-
题型
区间 D P DP DP
大佬普通写法