[CSP-S2019] 括号树
算法
特殊性质
显然链的情况就是括号匹配
因此显然有代码
代码
#include <bits/stdc++.h>
#define int long long
const int MAXN = 5e5 + 20;
int n;
std::string Braket;
int fa[MAXN];
bool Check_Special_Quality()
{
for (int i = 2; i <= n; i++)
{
if (fa[i] != i - 1)
{
return false;
}
}
return true;
}
std::stack<int> Pre;
int k[MAXN];
int Sum[MAXN];
int calc()
{
int ans = 0;
for (int i = 1; i <= n; i++)
{
// printf("%d ", k[i]);
ans = ans ^ (i * Sum[i]);
}
return ans;
}
void Solve_For_Special_Quality()
{
for (int i = 1; i <= n; i++)
{
if (Braket[i] == '(')
{
Pre.push(i);
}
else if (Braket[i] == ')')
{
if (Pre.empty())
{
}
else
{
int Now = Pre.top();
Pre.pop();
k[i] = k[Now - 1] + 1;
}
}
Sum[i] = Sum[i - 1] + k[i];
}
printf("%lld", calc());
}
signed main()
{
scanf("%lld", &n);
std::cin >> Braket;
Braket = ' ' + Braket;
for (int i = 2; i <= n; i++)
{
scanf("%lld", &fa[i]);
}
if (Check_Special_Quality())
{
Solve_For_Special_Quality();
return 0;
}
return 0;
}
注意不能用单数组, 因为不能计算反括号对应正括号
正解
显然有树状的递推性质
那么有
\[lst_i = lst_{t - 1} + 1 \rightarrow lst_x = lst_{\text{fa of x}} + 1
\]
代码
#include<bits/stdc++.h>
#define orz 0
#define inf 0x3f3f3f3f
#define ll long long
#define maxn 500005;
using namespace std;
int n;
char c[maxn];
int head[maxn], nxt[maxn], to[maxn], cnt, fa[maxn];
ll lst[maxn], sum[maxn], ans;
int s[maxn], top;
void add_edge(int u, int v)
{
nxt[++ cnt] = head[u];
head[u] = cnt;
to[cnt] = v;
}
void dfs(int x)
{
int tmp = 0;
if(c[x] == ')')
{
if(top)
{
tmp = s[top];
lst[x] = lst[fa[tmp]] + 1;
-- top;
}
}
else if(c[x] == '(') s[++ top] = x;
sum[x] = sum[fa[x]] + lst[x]; //如上所述
for(int i = head[x]; i; i = nxt[i])
dfs(to[i]); //递归
//回溯复原操作
if(tmp != 0) s[++ top] = tmp; //不为 0 代表有信息被弹出
else if(top) -- top;
//为 0 代表没有弹出,如果栈不为空说明一定压入了一个信息,需要弹出这个信息复原
}
int main()
{
scanf("%d", &n);
scanf("%s", c + 1);
for(int i = 2; i <= n; i ++)
{
int f;
scanf("%d", &f);
add_edge(f, i);
fa[i] = f;
}
dfs(1);
for(int i = 1; i <= n; i ++)
ans ^= sum[i] * (ll)i;
printf("%lld", ans);
return orz;
}
ctj 是这样的
总结
打特殊性质记得推下样例
特殊套路 :
dfs 时保持状态稳定