[CSP-S2019] 括号树
题目背景
本题中合法括号串的定义如下:
()
是合法括号串。- 如果
A
是合法括号串,则(A)
是合法括号串。 - 如果
A
,B
是合法括号串,则AB
是合法括号串。
本题中子串与不同的子串的定义如下:
- 字符串
S
的子串是S
中连续的任意个字符组成的字符串。S
的子串可用起始位置 l l l 与终止位置 r r r 来表示,记为 S ( l , r ) S (l, r) S(l,r)( 1 ≤ l ≤ r ≤ ∣ S ∣ 1 \leq l \leq r \leq |S | 1≤l≤r≤∣S∣, ∣ S ∣ |S | ∣S∣ 表示 S 的长度)。 S
的两个子串视作不同当且仅当它们在S
中的位置不同,即 l l l 不同或 r r r 不同。
题目描述
一个大小为 n n n 的树包含 n n n 个结点和 n − 1 n - 1 n−1 条边,每条边连接两个结点,且任意两个结点间有且仅有一条简单路径互相可达。
小 Q 是一个充满好奇心的小朋友,有一天他在上学的路上碰见了一个大小为 n n n 的树,树上结点从 1 ∼ n 1 \sim n 1∼n 编号, 1 1 1 号结点为树的根。除 1 1 1 号结点外,每个结点有一个父亲结点, u u u( 2 ≤ u ≤ n 2 \leq u \leq n 2≤u≤n)号结点的父亲为 f u f_u fu( 1 ≤ f u < u 1 ≤ f_u < u 1≤fu<u)号结点。
小 Q 发现这个树的每个结点上恰有一个括号,可能是(
或)
。小 Q 定义
s
i
s_i
si 为:将根结点到
i
i
i 号结点的简单路径上的括号,按结点经过顺序依次排列组成的字符串。
显然 s i s_i si 是个括号串,但不一定是合法括号串,因此现在小 Q 想对所有的 i i i( 1 ≤ i ≤ n 1\leq i\leq n 1≤i≤n)求出, s i s_i si 中有多少个互不相同的子串是合法括号串。
这个问题难倒了小 Q,他只好向你求助。设
s
i
s_i
si 共有
k
i
k_i
ki 个不同子串是合法括号串, 你只需要告诉小 Q 所有
i
×
k
i
i \times k_i
i×ki 的异或和,即:
(
1
×
k
1
)
xor
(
2
×
k
2
)
xor
(
3
×
k
3
)
xor
⋯
xor
(
n
×
k
n
)
(1 \times k_1)\ \text{xor}\ (2 \times k_2)\ \text{xor}\ (3 \times k_3)\ \text{xor}\ \cdots\ \text{xor}\ (n \times k_n)
(1×k1) xor (2×k2) xor (3×k3) xor ⋯ xor (n×kn)
其中
x
o
r
xor
xor 是位异或运算。
输入格式
第一行一个整数 n n n,表示树的大小。
第二行一个长为
n
n
n 的由(
与)
组成的括号串,第
i
i
i 个括号表示
i
i
i 号结点上的括号。
第三行包含 n − 1 n − 1 n−1 个整数,第 i i i( 1 ≤ i < n 1 \leq i \lt n 1≤i<n)个整数表示 i + 1 i + 1 i+1 号结点的父亲编号 f i + 1 f_{i+1} fi+1。
输出格式
仅一行一个整数表示答案。
样例 #1
样例输入 #1
5
(()()
1 1 2 2
样例输出 #1
6
提示
【样例解释1】
树的形态如下图:
将根到 1 号结点的简单路径上的括号,按经过顺序排列所组成的字符串为 (
,子串是合法括号串的个数为
0
0
0。
将根到 2 号结点的字符串为 ((
,子串是合法括号串的个数为
0
0
0。
将根到 3 号结点的字符串为 ()
,子串是合法括号串的个数为
1
1
1。
将根到 4 号结点的字符串为 (((
,子串是合法括号串的个数为
0
0
0。
将根到 5 号结点的字符串为 (()
,子串是合法括号串的个数为
1
1
1。
【数据范围】
思路
对于这种有括号的问题,我们都是用栈来维护的,因此我们可以在dfs的时候使用:
对于树上的括号,还是跟线性的一样,用栈,然后用 f[i]
数组来跳,只不过在树上是 f[i]
表示数组f[u]表示以u结尾的新增的合法子串数。如果是线性的,那就是以 i
结尾的xxxx数目。f[u]=f[fa[s[top]]]+1
这句话的含义是,到u的新增合法子串,要么是由栈顶父亲那里的合法串加上[s[top],u][s[top],u]
这一对括号构成的,要么就是[s[top],u][s[top],u]
这一对括号),并pop掉栈顶.其中:1到父亲的合法子串数为res。注意:我们算的是到u的合法子串数目,如果后面没有新增的话,那就是累加前面的,因为它也是合法子串。(也就是我
s
i
−
1
s_{i-1}
si−1 是满足条件的,那么我
s
i
s_i
si 也就满足条件)还有我们的答案就在线算吧。
还有要注意的就是,为什么要写 if(cnt)stk[++top]=cnt;
,是因为:
代码
#include<iostream>
#include<algorithm>
#include<cstring>
#define int long long
using namespace std;
const int N = 5e5+10,M = 2*N;
int e[M],ne[M],h[N],idx;
int f[N],fa[N];
int n;
char s[N];
int ans;
int stk[N];
int pre;
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int u,int fax,int top,int res){
int cnt=0;
if(s[u]=='('){
stk[++top]=u;
}else{
if(!top)f[u]=0;
else{
cnt=stk[top--];
f[u]=f[fa[cnt]]+1;
res+=f[u];
}
}
ans^=(res*u);
for(int i=h[u];~i;i=ne[i]){
int ver=e[i];
if(ver==fax)continue;
dfs(ver,u,top,res);
}
if(cnt)stk[++top]=cnt;
}
signed main(){
cin>>n>>(s+1);
memset(h,-1,sizeof h);
for(int i=2;i<=n;i++){
cin>>fa[i];
add(fa[i],i),add(i,fa[i]);
}
dfs(1,-1,0,0);
cout<<ans;
return 0;
}