P3757 [CQOI2017] 老C的键盘 的题解
考察:树形 DP、组合数学。
设
对于
若要求
前后缀优化后可以做到
::::success[证明]
定义
对于极大的小子树内部的合并,显然其内部的每个点对都会在其 LCA 处合并一次,故对于一棵小子树,合并时间复杂度
对于极大的小子树,到极小的大子树之间的合并,因为极大的小子树的大小不超过
对于极小的大子树,显然其棵数小于
综上,总时间复杂度仍为
::::success[代码]
#include<bits/stdc++.h>
#define endl '\n'
//#define MSOD
using namespace std;
using ll = long long;
const int N = 1e2 + 5;
const ll M = 1e9 + 7;
int n;
string s;
vector<pair<int, char>> T[N];
ll bin[N][N];
int siz[N];
ll f[N][N], g[N], pre[N][N], suf[N][N];
void dfs(int u) {
f[u][1] = 1, siz[u] = 1;
for(auto [v, w] : T[u]) {
dfs(v);
for(int i = 1 ; i <= siz[u] ; i ++) g[i] = f[u][i], f[u][i] = 0;
fill(f[u] + siz[u] + 1, f[u] + siz[u] + siz[v] + 1, 0);
if(w == '<') {
for(int k = 1 ; k <= siz[u] + siz[v] ; k ++) {
for(int j = max(0, k - siz[u]) ; j < min(k, siz[v]) ; j ++) {
f[u][k] += bin[k - 1][j] * bin[siz[u] + siz[v] - k][siz[v] - j] % M * g[k - j] % M * suf[v][j + 1] % M;
f[u][k] %= M;
}
}
} else {
for(int k = 1 ; k <= siz[u] + siz[v] ; k ++) {
for(int j = max(0, siz[v] - k) ; j < siz[v] + min(0, siz[u] - k) + 1 ; j ++) {
f[u][k] += bin[k - 1][siz[v] - j] * bin[siz[u] + siz[v] - k][j] % M * g[j + k - siz[v]] % M * pre[v][siz[v] - j] % M;
f[u][k] %= M;
}
}
}
siz[u] += siz[v];
}
for(int i = 1 ; i <= siz[u] ; i ++) pre[u][i] = pre[u][i - 1] + f[u][i], pre[u][i] %= M;
for(int i = siz[u] ; i >= 1 ; i --) suf[u][i] = suf[u][i + 1] + f[u][i], suf[u][i] %= M;
return;
}
void TEST() {
cin>>n>>s;
s = " " + s;
for(int i = 1 ; i < n ; i ++) T[(i + 1) / 2].emplace_back(i + 1, s[i]);
for(int i = 0 ; i <= n ; i ++) {
bin[i][0] = 1;
for(int j = 1 ; j <= i ; j ++) bin[i][j] = (bin[i - 1][j - 1] + bin[i - 1][j]) % M;
}
dfs(1);
ll ans = 0;
for(int i = 1 ; i <= n ; i ++) ans += f[1][i], ans %= M;
cout<<ans;
return;
}
signed main() {
ios::sync_with_stdio(false), cout.tie(0), cin.tie(0);
int TC = 1;
#ifdef MSOD
cin>>TC;
#endif
while(TC --) TEST();
return 0;
}
::::