括号树(CSP 2019 D1T2)

题目

题解

首先想到用单调栈将在树上以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;
}
发布了68 篇原创文章 · 获赞 7 · 访问量 3825

猜你喜欢

转载自blog.csdn.net/weixin_43823476/article/details/103199317