CF1748E Yet Another Array Counting Problem
简单笛卡尔树上 dp。
前置知识:笛卡尔树。
根据大根堆性质建出笛卡尔树,那么 $[l,r]$ 的“最左端最大值位置”就是 $l$ 和 $r$ 在笛卡尔树上的 $\textrm{lca}$。
若两个序列的所有“最左端最大值”都相等,那么意味着这两个序列所建出的笛卡尔树形态相同。转换为权值 $\le m$ 的笛卡尔树计数问题。
不妨设 $f_{u,j}$ 表示笛卡尔树上点 $u$ 权值为 $j$,其子树的答案。则有
$$f_{u,j}=\sum_{k=1}^{j-1}f_{lc_u,k}\times \sum_{k=1}^{j}f_{rc_u,k}$$
时间复杂度 $\Theta(\sum n\times m)$。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e6 + 10;
const int mod = 1e9 + 7;
int n, m, t;
int p[maxn];
int s[maxn], cnt;
int lc[maxn], rc[maxn];
vector<vector<int>> f;
void dfs(int u) {
for (int i = 1; i <= m; i++) f[u][i] = 1;
if (lc[u]) dfs(lc[u]);
if (rc[u]) dfs(rc[u]);
if (lc[u]) for (int i = 1; i <= m; i++) f[u][i] = f[u][i] * f[lc[u]][i - 1] % mod;
if (rc[u]) for (int i = 1; i <= m; i++) f[u][i] = f[u][i] * f[rc[u]][i] % mod;
for (int i = 2; i <= m; i++) f[u][i] = (f[u][i] + f[u][i - 1]) % mod;
}
signed main() {
cin >> t;
while (t--) {
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> p[i];
for (int i = 1; i <= n; i++) {
int top = cnt;
while (top && p[s[top]] < p[i]) --top;
if (top) rc[s[top]] = i;
if (top < cnt) lc[i] = s[top + 1];
s[cnt = ++top] = i;
}
f.resize(n + 1);
for (int i = 1; i <= n; i++) f[i].resize(m + 1);
dfs(s[1]);
cout << f[s[1]][m] << endl;
for (int i = 1; i <= cnt; i++) s[i] = 0;
for (int i = 1; i <= n; i++) lc[i] = rc[i] = 0;
cnt = 0;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现