洛谷P5658 括号树(CSP-S 2019 D1T2)题解 动态规划

题目链接:https://www.luogu.com.cn/problem/P5658

解题思路:

我们不妨将这道题进行一下简化,变成如下题目描述:

给你一个字符串,求这个字符串中有多少合法括号子串,看看能不能在O(n)时间复杂度内解决这个问题。

对于这样一个问题,我们可以用栈存储所有没有匹配上的 ‘(’ 对应的坐标,然后用数组 match[i] 表示当前位置 i 对应的刚好匹配上的左括号的坐标(当然前提是栈中有元素且当前元素 s[i] == ')')。
然后我们用 sum[i] 表示以 s[i] 结尾的合法括号子串的数量,则:如果 match[i] != 0sum[i] = sum[match[i]-1] + 1
答案就是 \(\sum{sum[i]}\)
实现代码如下:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1000010;
stack<int> stk;
int match[maxn];
char s[maxn];
long long sum[maxn], ans;
int main() {
    scanf("%s", s+1);
    int n = strlen(s+1);
    for (int i = 1; i <= n; i ++) {
        if (s[i] == '(') stk.push(i);
        else {
            if (!stk.empty()) {
                int p = stk.top();
                stk.pop();
                sum[i] = sum[p-1] + 1;
            }
        }
    }
    for (int i = 1; i <= n; i ++) ans += sum[i];
    printf("%lld\n", ans);
    return 0;
}

同样的思路,放到树上,只要在dfs的过程中进行同样的处理即可。

实现代码如下:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1000010;
int n, match[maxn], stk[maxn], sp[maxn], f[maxn], sz;
long long sum[maxn], tot[maxn], ans;
char s[maxn];
vector<int> g[maxn];
void dfs(int u, int id) {
    if (s[u] == '(') {
        sp[u] = id;
        id = u;
    }
    else {
        if (id) {
            match[u] = id;
            sum[u] = 1 + sum[f[match[u]]];
            id = sp[id];
        }
    }
    tot[u] = tot[f[u]] + sum[u];
    int sz = g[u].size();
    for (int i = 0; i < sz; i ++) {
        int v = g[u][i];
        dfs(v, id);
    }
}
int main() {
    scanf("%d%s", &n, s+1);
    for (int i = 2; i <= n; i ++) {
        scanf("%d", f+i);
        g[ f[i] ].push_back(i);
    }
    dfs(1, 0);
    for (int i = 1; i <= n; i ++)
        ans ^= (long long) i * tot[i];
    printf("%lld\n", ans);
    return 0;
}

当然这里处理的时候要注意一些细节,其中最不方便思考的一个问题是:栈如何保存。

所以这里我单独开了一个 \(sp[i]\) 用于表示在 \(i\) 刚添加进栈的时刻之前栈顶元素的坐标。

\(sum[i]\) 表示的还是以 \(s[i]\) 结尾的合法括号串数量。

\(tot[i]\) 表示的是根节点到 \(i\) 号节点的所有点的 \(sum\) 值之和。

最终的答案就是所有的 \(i \times tot[i]\) 的异或和。

当然这里还需要注意一点就是其实我的 \(match[]\) 数组可以不开,但是开着方便理解,所以我就没有去掉了。

posted @ 2020-06-15 17:04  quanjun  阅读(209)  评论(0编辑  收藏  举报