题目
题解
首先想到用单调栈将在树上以i结尾的括号串记录下来
然后进行分类讨论
如果第i个字符是(,则i的答案就是i的父亲的答案,是不变的
否则,这个有括号可能会对答案有贡献:
如果在这之前,没有其它的未匹配的右括号在(到i为止)最近出现的左括号与i之间,那么贡献就会加1
举例子:()() 如果i=4,那么贡献加一
否则:())) 如果i=4,那么贡献不变
但是还要考虑到像()()()这样的情况,可以用前缀和解决
sum[i]表示到i为止,有多少个括号()是串联的
具体就是最近出现的左括号的序号的父亲的前缀
如果匹配成功,记得将这个左括号消掉,则后面的右括号只能与更前面的左括号匹配
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <algorithm>
using namespace std;
#define ll long long
const int MAXN = 5e5 + 3;
int n , fa[MAXN];
vector<int>G[MAXN];
ll sum[MAXN] , ans[MAXN];
int sta[MAXN] , cnt;
int zf[MAXN] , cntz;
char a[MAXN];
void dfs( int x ){
for( int i = 0; i < G[x].size() ; i ++ ){
int v = G[x][i];
if( v == fa[x] ) continue;
ans[v] = ans[x];
if( a[v] == '(' ){
sta[++cnt] = v;
zf[++cntz] = v;
dfs( v );
sta[cnt--] = 0;zf[cntz--] = 0;
continue;
}
int y = zf[cntz];
bool fl = 0;
if( cntz > 0 )
ans[v] ++ , zf[cntz--]= 0 , fl = 1;
ans[v] += sum[fa[y]];
sum[v] = sum[fa[y]] + ( fl == 1 ? 1 : 0 );
sta[++cnt] = v;
dfs( v );
sum[v] = 0;
sta[cnt--] = 0;
if( fl )
zf[++cntz] = y;
}
}
int main()
{
scanf( "%d" , &n );
scanf( "%s" , a + 1 );
for( int i = 2 ; i <= n ; i++ ){
scanf( "%d" , &fa[i] );
G[fa[i]].push_back( i );
G[i].push_back( fa[i] );
}
sum[1] = ans[1] = 0;
sta[++cnt] = 1;
if( a[1] == '(' )
zf[++cntz] = 1;
dfs( 1 );
ll o = 1ll * ans[1] * 1;
for( int i = 2 ; i <= n ; i ++ )
o = o ^ ( 1ll * i * ans[i] );
printf( "%lld" , o );
return 0;
}