其他分享
首页 > 其他分享> > AcWing284 金字塔

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]\)。

image

综上,当 \(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