洛谷P5658 括号树,2019 CCF CSP-S2 day1第二题

题目链接

https://www.luogu.com.cn/problem/P5658

题解

通过一个dfs求出以i节点为结尾的合法括号数量(用val[i]表示),由于要求的是子串(开始没注意),所以又用了一个dfs更新val[i]的值为从根节点到i的合法子串数量,并求出答案,注意要开long long。
最大的问题在于第一个dfs怎么求出val[i],只需用一个数组表示i前一个没有消掉的位置(即p[i],这样就不用再用栈了),当求val[i]时,只要判断它前一个括号能否消除(具体看代码,判断较多)

代码

#include
using namespace std;
int n,fa[500005],p[500005];
vector<int> v[500005];
char s[500005];
long long ans=0,val[500005];
void getp(int x)
{
    if(x==1)
    {
        p[x]=0;
        return ;
    }

    if(val[fa[x]]==0)
    {
        //当val[fa[x]]==0则表示x父节点没有消除,那么就判断x和它的父节点是否能消除
        if(s[fa[x]-1]=='('&&s[x-1]==')')
        {
            //当x和它父节点能抵消时val[x]的值是它父节点的父节点的val值+1
            val[x]=val[fa[fa[x]]]+1;
            //父节点已经消除,则x的前驱节点(前一个没被消除的点)为x的父节点的前驱节点
            p[x]=p[fa[x]];
        }
        else
        {
            //父节点没有消除,则x的前驱节点为x的父节点
            p[x]=fa[x];
        }
    }
    else
    {
        //当val[fa[x]]!=0则表示x父节已经消除,则需要和父节点的前驱节点(即p[父节点])判断
        if(s[x-1]==')'&&s[p[fa[x]]-1]=='(')
        {
            //同理,val[x]的值是val[父节点的前驱节点的父节点]+1
            val[x]=val[fa[p[fa[x]]]]+1;
            p[x]=p[p[fa[x]]];
        }
        else
        {
            p[x]=p[fa[x]];
        }
    }
}
void dfs(int x)
{
    getp(x);
    for(int i=0;i<v[x].size();i++)
        dfs(v[x][i]);
}
void dfs2(int x)
{
    for(int i=0;i<v[x].size();i++)
    {
        val[v[x][i]]+=val[x];
        dfs2(v[x][i]);
    }
    ans^=(x*val[x]);
}
int main(){
    //freopen("brackets.in","r",stdin);
    //freopen("brackets.out","w",stdout);

    cin>>n;
    cin>>s;
    memset(val,0,sizeof(val));
    for(int i=2;i<=n;i++)
    {
        scanf("%d",&fa[i]);
        v[fa[i]].push_back(i);
    }
    p[1]=0;
    fa[1]=0;
    dfs(1);
    dfs2(1);
    cout<<ans<<endl;


    //fclose(stdin);
    //fclose(stdout);
    return 0;
}

你可能感兴趣的:(洛谷P5658 括号树,2019 CCF CSP-S2 day1第二题)