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;
}
posted @   TernaKagiri  阅读(9)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示