AcWing284 金字塔
作者:互联网
题目大意
有一棵树,每个节点有一个颜色,用字母 A
~ Z
表示。现在从树根开始进行深度优先遍历,每来到一个节点会记录这个节点的颜色,最后在树根结束遍历。
显然,每个节点会访问至少一次,并且穿越每条边恰好两次(两个方向各一次), 然后,你会得到一个颜色序列。但是,你会发现这个颜色序列并不能唯一确定树的结构。
现在请你计算,对于一个给定的颜色序列,有多少种可能的树形结构会得到这个序列。因为结果可能会非常大,你只需要输出答案对 \(10^9\) 取模之后的值。
大体思路
考虑区间 DP。我们知道子树的 dfn 是一段连续的区间。因此,原颜色串的区间 \([l,r]\),如果满足 \(s_l=s_r\),则说明这一段 dfn 区间可能对应原来的一棵子树。
记 \(F[i,j]\) 表示区间 \([i,j]\) 能形成的树形结构数量。当 \(s_i\neq s_j\) 时,\(F[i,j]=0\)。
当 \(s_i=s_j\) 时,设这一段颜色序列为 A.......A
。那么,中间的一段可能作为单独一棵子树,方案数为 \(F[i+1,j-1]\)。
然后,为了避免重复,枚举最左边的一棵子树 \(x\) 结束的位置 \(k\),则 \([i+1,k-1]\) 描述了子树 \(x\),\([k,j]\) 成为了一个与 \([i,j]\) 相似的子问题。由于\(i\sim k,k\sim j\) 非空,所以 \(F[i,j]=\sum_{k=i+2}^{j-2} F[i+1,k-1]\times F[k,j]\ [s_i=s_k]\)。
发现 \([i+1,j-1]\) 相当于右边为空,即全部为一棵子树,可以将上述两个合并成 \(F[i,j]=\sum_{k=i+2}^{j} F[i+1,k-1]\times F[k,j]\ [s_i=s_k]\)。
综上,当 \(s_i=s_j\) 时,
\[F[i,j]=\sum_{i+1\le k\le j, s_i=s_k} F[i+1,k-1]\times F[k,j] \]边界条件 \(F[i,i]=0\),答案为 \(F[1,n]\),时间复杂度 \(O(n^3)\)。
代码
#include <bits/stdc++.h>
using namespace std;
#define rep(ii,aa,bb) for(re int ii = aa; ii <= bb; ii++)
#define Rep(ii,aa,bb) for(re int ii = aa; ii >= bb; ii--)
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef pair<int, int> PII;
const int maxn = 305;
const ll mod = 1e9;
namespace IO_ReadWrite {
#define re register
#define gg (p1 == p2 && (p2 = (p1 = _buf) + fread(_buf, 1, 1<<21, stdin), p1 == p2) ? EOF :*p1++)
char _buf[1<<21], *p1 = _buf, *p2 = _buf;
template <typename T>
inline void read(T &x){
x = 0; re T f=1; re char c = gg;
while(c > 57 || c < 48){if(c == '-') f = -1;c = gg;}
while(c >= 48 &&c <= 57){x = (x<<1) + (x<<3) + (c^48);c = gg;}
x *= f;return;
}
inline void ReadChar(char &c){
c = gg;
while(!isalpha(c)) c = gg;
}
template <typename T>
inline void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x/10);
putchar('0' + x % 10);
}
template <typename T>
inline void writeln(T x){write(x); putchar('\n');}
}
using namespace IO_ReadWrite;
char s[maxn];
ll f[maxn][maxn];
int main () {
scanf("%s", s + 1);
int n = strlen(s + 1);
rep(i, 1, n) f[i][i] = 1;
rep(len, 2, n)
for(int i = 1, j = len; j <= n; i ++, j ++) {
if(s[i] != s[j] || !(len & 1)) {
f[i][j] = 0;
continue;
}
f[i][j] = f[i + 1][j - 1] % mod;
rep(k, i + 2, j - 2) if(s[i] == s[k])
f[i][j] = (f[i][j] + f[i + 1][k - 1] * f[k][j] % mod) % mod;
}
writeln(f[1][n] % mod);
return 0;
}
标签:typedef,子树,AcWing284,int,long,re,maxn,金字塔 来源: https://www.cnblogs.com/Mars-LG/p/AcWing284.html