题目
算法标签: 树形 d p dp dp, 栈
思路
首先将问题简化, 将问题转化为序列, 我们发现每一个右括号匹配的做括号是唯一确定的, 那么就有状态表示 f [ i ] f[i] f[i]表示在前 i i i个集合当中的合法括号序列的集合, 属性就是方案数, 将集合按照最后一个括号是否选择进行划分, 如果包含第 i i i个括号一定是右括号, 如果是左括号无法无法匹配

将序列推广到树, 可以维护当前括号到根节点的括号序列, 可以使用栈维护序列, 时间复杂度 O ( n ) O(n) O(n)序列最长 5 × 1 0 5 5 \times 10 ^ 5 5×105, 因为是连续的所以位置 j j j是 f [ j − 1 ] − f [ j − 2 ] + 1 f[j - 1] - f[j - 2] + 1 f[j−1]−f[j−2]+1, 也就是求出了以 j j j结尾的合法括号序列的数量
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 5e5 + 10;
int n;
vector<int> head[N];
string s;
int fa[N];
LL f[N];
int stk[N], top;
void add(int u, int v) {
head[u].push_back(v);
}
void dfs(int u) {
if (s[u] == '(') {
stk[++top] = u;
f[u] = f[fa[u]];
for (int v : head[u]) dfs(v);
top--;
}
else {
if (!top) {
f[u] = f[fa[u]];
for (int v : head[u]) dfs(v);
}
else {
int tmp = stk[top--];
f[u] = f[fa[u]] + f[fa[tmp]] - f[fa[fa[tmp]]] + 1;
for (int v : head[u]) dfs(v);
stk[++top] = tmp;
}
}
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> s;
s = " " + s;
for (int i = 2; i <= n; ++i) {
cin >> fa[i];
add(fa[i], i);
}
dfs(1);
LL res = 0;
for (int i = 1; i <= n; ++i) res ^= i * f[i];
cout << res << "\n";
return 0;
}


京公网安备 11010502036488号