【题解】LuoGu5658:括号树

原题传送门
树上括号序列匹配问题
考场上写了个 O ( n 2 ) O(n^2) O(n2)暴力加上优化过掉了
思路如下:

  • 树形dp想法,令 c n t u cnt_u cntu表示从1到 u u u的字符串中以 u u u结尾的合法子串有多少个
  • 那么可以想到,如果当前是个 ( ( (,那么不存在以这个结尾的合法子串
  • ( ( (看成1, ) ) )看成-1,比如 ( ) ( ) ) ()()) ()())的权值就是-1
  • 转移:令 s i s_i si表示从1到 i i i的权值,那么若存在 s u = s v s_u=s_v su=sv,那么对于 u u u,祖先中深度最大的满足上式的 v v v就是要找的,之后 c n t u = c n t v + 1 cnt_u=cnt_v+1 cntu=cntv+1
  • dfs遍历的同时记录不以 u u u结尾的合法子串总数 s u m sum sum,当前总和就是 s u m + c n t u sum+cnt_u sum+cntu
  • 对于每个 u u u O ( n ) O(n) O(n)找到对应的 v v v

确实是过掉了了,但是严格意义上此题我并没有做出来,所以正解如下:

  • 关于快速找到那个 v v v,我们进行优化,可以开个桶, l s t i lst_i lsti表示 s = i s=i s=i的点中最晚出现的那个
  • 这样总体时间就是 O ( n ) O(n) O(n)

Code:

#include 
#define maxn 500010
#define LL long long
using namespace std;
struct Edge{
	int to, next;
}edge[maxn << 1];
LL ans, cnt[maxn];
int lst[maxn << 1], fa[maxn], num, head[maxn], n, val[maxn];

inline int read(){
	int s = 0, w = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
	for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
	return s * w;
}

inline int get(){
	char c = getchar();
	for (; c != '(' && c != ')'; c = getchar());
	return c == '(';
}

void addedge(int x, int y){ edge[++num] = (Edge){y, head[x]}, head[x] = num; }

void dfs(int u, LL sum, int s){
	if (val[u]) ++s; else --s;
	if (!val[u] && (lst[s + n] || s == 0)) cnt[u] = cnt[lst[s + n]] + 1;
	int tmp = lst[s + n];
	lst[s + n] = u;
	ans ^= (1LL * u * (sum += cnt[u])); //printf("%d %d\n", u, sum);
	for (int i = head[u]; i; i = edge[i].next) dfs(edge[i].to, sum, s);
	lst[s + n] = tmp;
}

int main(){
	n = read();
	for (int i = 1; i <= n; ++i) val[i] = get();
	for (int i = 2; i <= n; ++i) addedge(fa[i] = read(), i);
	dfs(1, 0, 0);
	printf("%lld\n", ans);
	return 0;
}

你可能感兴趣的:(题解,noip,LuoGu,题解,NOIp)