6431. 【NOIP2019提高组正式赛day1】括号树(brackets)

Description

Input

Output

输出到文件 brackets.out 中。
仅一行一个整数表示答案。 

Sample Input

5
(()()
1 1 2 2

Sample Output

6

Data Constraint

Solution

树上括号问题,题目大意是:给你一棵树,让你求对于每一个点,从根节点到这个点的连续子序列为合法括号序的数量的总和。

不会正解。

只打了个n^2+n log n 的解法,比赛后才发现不用那个log,直接n^2。

枚举节点,直接计算上面的答案加上以这个点为结尾的合法括号序的答案即可。

至于正解,先设g[i]表示 i 以这个点为结尾能往前找到的合法括号序的个数,那么每次我们找到最深的一个点使得这个点到当前dg到的x这一段括号序是合法的(即前缀和a[x]==a[y]),那么这个点x的答案可以由找到的点y的父亲的g值+1转移过来,因为上一段是合法的接上这一段合法的括号序仍然是合法的,+1是y~x这一段括号序。

因此可以用倍增维护从当前这个点到上面的区间最小值。

当a[x]小于等于倍增跳到的点时就可以结束了,因为等于时找到了y,而小于时则是不合法的括号序。

第二种方法则是设last[x]表示从根节点到x中深度最深的没有匹配过的左括号的编号,当dfs到的这个点是左括号是,它的last就是它自己,而是右括号时,它就可以用它与它父亲的last匹配,那么它的last就是它父亲的last的父亲的last。

这样就直接建立了递推关系直接进行递推。

Code1

#include<cstdio>
#include<cstring>
#include<algorithm>
#define I int
#define ll long long
#define F(i,a,b) for(I i=a;i<=b;i++)
#define Fd(i,a,b) for(I i=a;i>=b;i--)
#define mem(a,b) memset(a,b,sizeof(a))
#define N 1000010
using namespace std;
I n,x,t[N*2],nx[N*2],l[N],a[N],tot;
I s[N][20],f[N][20],d[N*2],bz[N];
ll ans,g[N];
char ch;
void add(I x,I y){t[++tot]=y;nx[tot]=l[x];l[x]=tot;}
void rd(I &x){
	x=0;ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
}
void dfs(I x,I y){
	mem(bz,0);
	I i=1,j=1;d[1]=1;bz[1]=1;
	while(i<=j){
		x=d[i++];
		for(I k=l[x];k;k=nx[k]) if(!bz[t[k]]){
			bz[t[k]]=1;
			d[++j]=t[k];
			f[t[k]][0]=x;s[t[k]][0]=a[x];
			a[t[k]]+=a[x];
		}
	}
}
void dg(I x,I y){
	mem(bz,0);
	I he=1,ta=1;d[1]=1;bz[1]=1;
	while(he<=ta){
		I x=d[he++],z=x;
		Fd(i,19,0){
			if(s[z][i]>a[x]) z=f[z][i];
		}
		if(a[f[z][0]]==a[x]){
			g[x]+=g[f[z][0]]-g[f[f[z][0]][0]]+1;
		}
		for(I k=l[x];k;k=nx[k]) if(!bz[t[k]]){
			bz[t[k]]=1;
			d[++ta]=t[k];
			g[t[k]]+=g[x];
		}
	}
} 
I main(){
	freopen("brackets.in","r",stdin);
	freopen("brackets.out","w",stdout);
	rd(n);
	F(i,1,n){
		ch=getchar();
		while(ch!=')'&&ch!='(') ch=getchar();
		if(ch=='(') a[i]=1;
		else a[i]=-1;
	}
	F(i,2,n){
		rd(x);
		add(x,i);
		add(i,x);
	}
	dfs(1,0);
	F(j,1,19){
		F(i,1,n){
			f[i][j]=f[f[i][j-1]][j-1];
			s[i][j]=min(s[i][j-1],s[f[i][j-1]][j-1]);
		}
	}
	dg(1,0);
	F(i,1,n) ans^=(i*g[i]);
	printf("%lld\n",ans);
	return 0;
}

Code2

#include<cstdio>
#include<cstring>
#include<algorithm>
#define I int
#define ll long long
#define F(i,a,b) for(I i=a;i<=b;i++)
#define Fd(i,a,b) for(I i=a;i>=b;i--)
#define mem(a,b) memset(a,b,sizeof(a))
#define N 1000010
using namespace std;
I n,x,t[N*2],nx[N*2],l[N],a[N],s[N],tot,f[N],p,fa[N];
ll ans,g[N];
char ch;
void add(I x,I y){t[++tot]=y;nx[tot]=l[x];l[x]=tot;}
void rd(I &x){
	x=0;ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
}
void dg(I x,I y){
	s[x]=a[x]+s[y];g[x]+=g[y];
	if(a[x]==1) f[x]=x;
	else{
		p=f[y];
		if(p) g[x]+=g[fa[p]]-g[fa[fa[p]]]+1;
		f[x]=f[fa[p]];
	}
	ans^=(x*g[x]);
	for(I k=l[x];k;k=nx[k]){
		if(t[k]!=y){
			dg(t[k],x);
		}
	}
} 
I main(){
	freopen("brackets.in","r",stdin);
	freopen("brackets.out","w",stdout);
	rd(n);
	F(i,1,n){
		ch=getchar();
		while(ch!=')'&&ch!='(') ch=getchar();
		a[i]=ch=='('?1:-1;
	}
	F(i,2,n){rd(x);fa[i]=x;add(x,i);add(i,x);}
	dg(1,0);
	printf("%lld\n",ans);
	return 0;
}


作者:zsjzliziyang 
QQ:1634151125 
转载及修改请注明 
本文地址:https://blog.csdn.net/zsjzliziyang/article/details/103321788

发布了199 篇原创文章 · 获赞 201 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/zsjzliziyang/article/details/103321788