括号树 解题报告

题目背景

本题中合法括号串的定义如下:

  1. ()是合法括号串。
  2. 如果A是合法括号串,则(A)是合法括号串。
  3. 如果AB是合法括号串,则AB是合法括号串。

本题中子串不同的子串的定义如下:
4. 字符串S的子串是S中连续的任意个字符组成的字符串。S的子串可用起始位置ll与终止位置rr来表示,记为S(l,r)(1lrSS(l,r)(1≤l≤r≤|S|S|S|表示S的长度)。
5. S的两个子串视作不同当且仅当它们在S中的位置不同,即ll不同或rr不同。

题目描述

一个大小为nn的树包含nn个结点和n1n−1条边,每条边连接两个结点,且任意两个结点间有且仅有一条简单路径互相可达。小Q是一个充满好奇心的小朋友,有一天他在上学的路上碰见了一个大小nn的树,树上结点从1n1-n编号,11号结点为树的根。除11号结点外,每个结点有一个父亲结点,u(2un)u(2≤u≤n)号结点的父亲为fu1fu<u)f_u(1≤fu<u)号结点。小Q发现这个树的每个结点上恰有一个括号,可能是()。小Q定义sis_i为:将根结点到ii号结点的简单路径上的括号,按结点经过顺序依次排列组成的字符串。显然sis_i是个括号串,但不一定是合法括号串,因此现在小Q想对所有的i(1in)i(1≤i≤n)求出,sis_i中有多少个互不相同的子串是合法括号串。这个问题难倒了小Q,他只好向你求助。设sis_i共有 kik_i个不同子串是合法括号串, 你只需要告诉小Q所有i×kii \times k_i的异或和,即:(1×k1)xor(2×k2)xor(3×k3)xorxor(n×kn)(1×k_1) \operatorname{xor} (2×k_2) \operatorname{xor} (3×k_3) \operatorname{xor} ⋯ \operatorname{xor} (n×k_n)。其中xor\operatorname{xor}是位异或运算。

输入格式

第一行一个整数nn,表示树的大小。第二行一个长为nn的由()组成的括号串,第ii个括号表示ii号结点上的括号。第三行包含n1n−1个整数,第 i(1i<n)i(1≤i<n)个整数表示i+1i+1号结点的父亲编号fi+1f_{i+1}​。

输出格式

仅一行一个整数表示答案。

题解

本题考察的知识点比较全面,是一道优秀的NOIP(CSP-S)题目。

如果直接考虑树的情况,可能会感觉陌生而毫无思路。那么我们不妨考虑一条链上的情况,这样题目看上去非常可做,与一些典型题目也是类似的。

设第ii个贡献的子串数目(即以ii结尾的合法子串数目)为lst[i]lst[i],用栈储存当前未匹配的左括号。显然,如果ii为左括号,lst[i]=0lst[i]=0;如果为右括号,只能匹配栈顶的左括号,且这两个括号之间的子串必定也为合法子串。因为如果这个子串中有未匹配的左括号,则其必定在栈中更近栈顶;若有未匹配的右括号,则栈必空。那么,这一对括号匹配之后,与其中间子串共同构成了一个合法子串,因此lst[i]=1lst[i]=1

但是我们还得考虑该子串是否能和其前面的子串连接形成更多的合法子串,由于子串必须是连续的,因此我们只需要看左括号的上一位即可。假设左括号位置为ll,那么lst[i]lst[i]还需要加上lst[l1]lst[l-1],即最终结果为lst[i]=lst[l1]+1lst[i]=lst[l-1]+1

那么题目中的kik_i,就是lstlst的前缀和,因此ki=ki1+lst[i]k_i=k_{i-1}+lst[i].

我们再考虑树上的情况,这时我们会发现,既然一条链的情况我们直接枚举,那么树的情况,我们只需从根节点dfsdfs遍历树,也可以枚举出所有的链,就可以求出所有kik_i,方法则和链的情况类似,只需把推导的l1l-1,i1i-1分别改成fa[l],fa[i]fa[l],fa[i]即可。

非常巧妙的一道题,乍一看没有思路,但按照数据范围的提示一步一步推导,其实也并不是那么难。

#include<cstdio>
using namespace std;
 struct forward_star
 {
  int next,to;
 };
 int n,cnt,top;
 int stack[500001];
 long long lst[500001];
 char a[500001];
 int fa[500001];
 int head[500001];
 long long dp[500001];
 forward_star edge[500001];
void add(int u,int v)
{
 cnt ++;
 edge[cnt].to = v;
 edge[cnt].next = head[u];
 head[u] = cnt;
}
void dfs(int now)
{
 int tmp = 0;
 bool flag = false;
 if (a[now] == '(')
  stack[++ top] = now;
 else
 {
  if (top)
  {
   flag = true;
   tmp = stack[top];
   top --;
   lst[now] = lst[fa[tmp]] + 1;
  }
 }
 dp[now] = dp[fa[now]] + lst[now];
 int i = head[now];
 while (i != 0)
 {
  dfs(edge[i].to);
  i = edge[i].next;
 }
 if (a[now] == '(')
  top --;
 if (flag)
  stack[++ top] = tmp;
}
int main()
{
 scanf("%d", &n);
 scanf("%s", a + 1);
 for (int i = 2;i <= n;i ++)
 {
  int x;
  scanf("%d", &x);
  fa[i] = x;
  add(x,i);
 }
 dfs(1);
 long long ans = 0;
 for (int i = 1;i <= n;i ++)
  ans ^= (long long)i * dp[i];
 printf("%lld",ans);
 return 0;
} 
发布了82 篇原创文章 · 获赞 21 · 访问量 4万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 书香水墨 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览