【NOIP/CSP2019】D1T2 括号树
原题:
因为是NOIP题,所以首先先看特殊数据,前35分是一条长度不超过2000的链,N^2枚举所有子区间暴力check就能拿到分
其次可以思考特殊情况,一条链的情况怎么做
OI系列赛事的特殊性质分很多时候不仅是帮助得分,还帮助选手找到思路
观察合法串的形状,可以发现主要由括号嵌套和并列组成
嵌套好说,一对匹配的括号对答案贡献为1(里边包的东西不合法的括号不算匹配的括号)
对于并列的括号,可以发现如果要把两对匹配的括号并列算作一个贡献,那么必须要求这两个括号挨着,即右边的左括号的左边是左边的右括号
思考涉及到子区间的问题时,一个常见的思路是确定一个端点,考虑另一个
因为括号匹配是从左到右添加进栈的,那么不妨确定右端点,对于加入的右括号我们只需考虑其能匹配多少个左端点
可以发现,某个右括号和跟他匹配的左括号算1个贡献
如果左括号的左边是匹配上的右括号,那么这个右括号作为右端点的合法区间都可以直接接上右边匹配的一对括号算作贡献1
那么思路就很清楚了,总结一下,只考虑对于匹配上的右括号,有多少个左端点使得区间合法
它自己的左括号算贡献1,然后再把右端点为左括号下标-1的合法区间数接上
用g[i]表示点i为右端点的方案数,f[i]表示g[i]的前缀和(用于统计答案)
那么如果某个点是左括号或匹配不上的右括号,g[i]为0
如果是匹配上左括号j的右括号,g[i]=g[j-1]+1
那么对于一棵树的情况,其实可以发现,只需要dfs树,然后回溯的时候把新加进来的括号退栈就跟序列的情况没什么区别
(当然别忘了刚才退栈的括号回溯时要补进)
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 using namespace std; 7 #define LL long long 8 int rd(){int z=0,mk=1; char ch=getchar(); 9 while(ch<'0'||ch>'9'){if(ch=='-')mk=-1; ch=getchar();} 10 while(ch>='0'&&ch<='9'){z=(z<<3)+(z<<1)+ch-'0'; ch=getchar();} 11 return z*mk; 12 } 13 struct edg{int nxt,y;}e[510000]; int lk[510000],ltp=0; 14 void ist(int x,int y){ e[++ltp]=(edg){lk[x],y}; lk[x]=ltp;} 15 int n,fth[510000]; char s[510000]; 16 LL f[510000],g[510000]; 17 int q[510000],hd=0; 18 void dfs(int x){ 19 //cout<<"x: "<<x<<" f: "<<f[x]<<" g: "<<g[x]<<endl; 20 //for(int i=1;i<=hd;++i) cout<<q[i]<<" "; 21 //cout<<endl; 22 for(int i=lk[x];i;i=e[i].nxt){ 23 int tmp=q[hd]; 24 if(s[e[i].y]==')' && hd){ 25 g[e[i].y]=g[fth[tmp]]+1; 26 f[e[i].y]=f[x]+g[e[i].y]; 27 --hd; 28 } 29 else{ 30 if(s[e[i].y]=='(') q[++hd]=e[i].y; 31 //因为栈里只会有左括号,所以存下标表示这里有个左括号就vans了 32 g[e[i].y]=0; 33 f[e[i].y]=f[x]; 34 } 35 dfs(e[i].y); 36 if(s[e[i].y]==')' && tmp) q[++hd]=tmp; 37 //注意不是hd!=0 38 else if(s[e[i].y]=='(') --hd; 39 } 40 } 41 int main(){ 42 freopen("ddd.in","r",stdin); 43 cin>>n; 44 scanf("%s",s+1); 45 for(int i=2;i<=n;++i) fth[i]=rd(),ist(fth[i],i); 46 //q[++hd]=1; f[1]=0,g[1]=0; 注意s[1]不一定是'(' 47 if(s[1]=='(') q[++hd]=1; f[1]=0,g[1]=0; 48 dfs(1); 49 LL ans=0; 50 for(int i=1;i<=n;++i) ans^=i*f[i]; 51 cout<<ans<<endl; 52 return 0; 53 }