金字塔 题解报告

题目传送门

【题目大意】

整个金字塔为一个有根树结构,根结点为入口,每个结点涂有一种颜色。机器人从入口开始进行DFS,每经过一个结点,它就会记录这个结点的颜色,最后形成一个序列,求对应这个序列可行的树的结构种类数。

【思路解析】

对于序列S,若子串S[l~r]对应一棵子树,则S[l+1]和S[r-1]是进入和离开时产生的。除此之外,[l,r]包含的每棵更深的子树都对应着一个子问题,会产生[l,r]中的一段,相邻两段之间还有途经树根产生的一个字符。因为[l,r]包含的子树个数可能不止两个,如果朴素地枚举子串S[l~r]划分点的数量和所有划分点的位置,那么时间复杂度会很高。

那么我们考虑换一种方法,只考虑子串S[l~r]的第一棵子树是由哪一段构成的。枚举划分点k,令子串S[l+1~k-1]构成[l,r]的第一棵子树,S[k~r]构成[l,r]的剩余部分(其他子树)。如果k不同,那么子串S[l+1~k-1]代表的子树的大小也不同,所以不可能出现重复计算的结果,并且我们可以得到转移方程(f[l][r]表示方案数):$$if(S[l] e S[r]) o f[l][r]=0$$

$$if(S[l]=S[r]) o f[l][r]=f[l+1][r-1]+sum_{l+2le kle r-2,S[l]=S[k]}f[l+1][k-1]*f[k][r]$$初始值:$forall lin [1,len(S)],f[l][l]=1$,其余均为0。

目标:f[1][len(S)]

我们发现,对于方案计数类问题,通常一个状态的各个决策之间满足“加法原理”,而每个决策划分的几个子状态之间满足“乘法原理”。在设计状态转移方程的决策方式与划分方法时,一个状态的所有决策之间必须具有互斥性,才能保证不会重复。

【代码实现】

 1 #include<bits/stdc++.h>
 2 #define rg register
 3 #define go(i,a,b) for(rg int i=a;i<=b;i++)
 4 #define back(i,a,b) for(rg int i=a;i>=b;i--)
 5 #define ll long long
 6 #define mem(a,b) memset(a,b,sizeof(a))
 7 using namespace std;
 8 const int N=302;
 9 const int mod=1e9;
10 int f[N][N];
11 char S[N];
12 int work(int l,int r){
13     if(l>r||S[l]!=S[r]) return 0;
14     if(l==r) return 1;
15     if(f[l][r]!=-1) return f[l][r];
16     f[l][r]=0;
17     go(k,l+2,r)
18         f[l][r]=(f[l][r]+(ll)work(l+1,k-1)*work(k,r))%mod;
19     return f[l][r];
20 }
21 int main(){
22     scanf("%s",S);
23     int n=strlen(S);
24     mem(f,-1);
25     printf("%d
",work(0,n-1));
26     return 0;
27 }
代码戳这里