括號樹(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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章