「CSPS 2019」括号树

【题目描述】

传送门

【题解】

  • 是时候讨论一下我在考场上是怎么将这道题写挂的了
  • 初看这道题毫无思路,先看看部分分吧
  • 一条链的情况?设k[i]表示前i个括号的方案数
  • 显然\(k[i]=k[i-1]+\)以i结尾的合法子串个数
  • 考虑求\(a[i]\)表示以\(i\)结尾的合法子串个数,显然如果第\(i\)个字符是\(‘('\),\(a[i]=0\)
  • 否则,看第\(i-1\)个字符,如果是\(‘(’\),则\(a[i]=a[i-2]\),否则就跳到与\(i-1\)匹配的\(b[i-1]\)处看\(b[i-1]-1\)是否是\('('\),如果不是,就再往前匹配······依次类推即可
  • 然而,我在考场上时,居然脑抽,只往前匹配了一次,并且就这样还通过了大样例···
//惨痛回忆
//d[i]=1表示是左括号,=-1表示是右括号
for(int i=1;i<=n;++i){
	if(d[i]==1){
		k[i]=k[i-1];
		continue;
	}
	if(i==1) continue;
	if(d[i-1]==1){
		b[i]=i-1;
		a[i]=a[i-2]+1;
	}
	else if(d[b[i-1]-1]==1) a[i]=a[b[i-1]-2]+1,b[i]=b[i-1]-1;
	k[i]=k[i-1]+a[i];
}
//正解
for(int i=1;i<=n;++i){
	if(d[i]==1){
		k[i]=k[i-1];
		continue;
	}
	if(i==1) continue;
	int now=i-1;
	while(d[now]!=1&&now)
		now=b[now]-1;
	if(now){
		a[i]=a[now-1]+1;
		b[i]=now;
	}
	k[i]=k[i-1]+a[i];
}
  • 正解,则只需要将链中通过-1向前推改为通过父子关系推即可

【代码】

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=5e5+10;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	return x*f;
}
int n,d[N],fa[N],a[N],b[N],k[N],ans;
signed main(){
	n=read();
	for(int i=1;i<=n;i++){
		char ch=getchar();
		while(ch!='('&&ch!=')') ch=getchar();
		if(ch=='(') d[i]=1;
		else d[i]=-1;
	}
	for(int i=2;i<=n;i++)
		fa[i]=read();
	for(int i=1;i<=n;i++){
		if(i==1) continue;
		if(d[i]==1) k[i]=k[fa[i]];
		else{	
			int now=fa[i];
			while(d[now]!=1&&now)
				now=fa[b[now]];
			if(now){
				a[i]=a[fa[now]]+1;
				b[i]=now;
			}
			k[i]=k[fa[i]]+a[i];
		}
		ans=ans^(i*k[i]);
	}
	cout<<ans<<endl;
	return 0;
}
原文地址:https://www.cnblogs.com/tqxboomzero/p/13817503.html