括号树题解

大家好,我是刘蕴熹,这是 本蒟蒻 第一次做提高组的T2。

好难呀!! 我真正感受到了T2的 威力 。

话说回来,大家请看阶梯得分:

第一阶梯:20+分

反正是 O(n^3) ,怎么搞都行,没什么难度,堪比红题。

这里介绍一种思路,可以先求出每个点所表示的括号串,然后暴力枚举子串左端点,然后扫一遍右端点,用一个变量 cnt 判断即可:  

  •  如果是 ‘)’,则 cnt--(如果cnt为负,则这个字符串不行,后面的都不行)。
  •  否则,cnt++。
  •  若操作完毕,cnt==0,则是合法的括号串。

代码详见第二阶梯(很类似)。

第二阶梯:50-分

其实也是 O(n^3),很玄学,其实加了个特判,何为特判?

实际上我们就可以发现,前面的第一条如过触发错误(为负),则我们跳出(其实它和前面的代码一样QwQ!!)。本蒟蒻阶梯失误

#include <bits/stdc++.h>
using namespace std;
string tree[500010];
int f[500010];  
string s;int n;   
long long ans=0;
int ceil(string s){
	int ans=0;
	for (int st=1; st<=s.size(); st++){
		int cnt=0;
		for (int i=st; i<=s.size(); i++){
			if (s[i-1]=='(') cnt++;
			else{
				if (cnt==0) break; 
				else cnt--;
			} 
			if (cnt==0) ans++;
		}
	}
	return ans;
}  
signed main(){
	cin>>n>>s;
	tree[1].push_back(s[0]);
	for (int i=2; i<=n; i++){
		cin>>f[i];
		tree[i]=tree[f[i]]+s[i-1];
		ans^=1LL*i*ceil(tree[i]); 
	}
	cout<<ans;
	return 0;
}

第三阶梯:55分 

本蒟蒻存有幻想,能不能 O(1) 查询? 

大胆猜测,小心求证

接下来我们可以开始求解了(先以链入手): 

举个例子:

1. ‘()()’

扫到第 2 个时,贡献了1个答案。

扫到第 4 个时,贡献了2个答案('()'和'()()')。

贡献序列 0102。

2.‘())()’

扫到第 2 个时,贡献了1个答案。

扫到第 3 个时,答案不变( ')'和'())'都不是 )。

扫到第 5 个时,贡献了1个答案。

贡献序列 01001。

3.‘(())()’

扫到第 3 个时,贡献了1个答案。

扫到第 4 个时,贡献了1个答案('(())')。

扫到第 6 个时,贡献了2个答案('(())()' 和 '()')。

贡献序列 001102。

总结

总结下来,可以发现,如果每个右括号可能会进行贡献。

贡献是几呢?其实,如果这个括号有人匹配(假设是第 j 个),那么它的的贡献是 j-1 号括号的贡献+1。

如何求出 j 呢,用栈不就行了?

代码就不给了,类似动态规划,第 i 个点的最终答案就是对贡献序列求前缀和的第 i 项,注意 long long。

第四阶梯:100分

对第三阶梯进行改进,稍加思考,我们可以发现,前面的 i-1 相当于这里的 f[i]。

但是还是有一个问题,如何进行遍历呢,而且还要保证不会因为遍历,而改变每个状态的栈?

其实做反操作就行了!如果入栈了,就把他扔掉,如果出栈了,就把他扔进来。

详情请看代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
vector <int> vc[500010];
int cnt[500010],ans[500010];
int n,f[500010]; string s;
stack <int> sk;
void dfs(int x){
	int k=0;
	if (s[x]==')'){
		if (!sk.empty()){
			k=sk.top(); sk.pop();
			cnt[x]=cnt[f[k]]+1;
		}
	}
	else sk.push(x);
	ans[x]=ans[f[x]]+cnt[x];
	for (auto v:vc[x]) dfs(v);
	if (k) sk.push(k);
	else if (!sk.empty()) sk.pop();
}
signed main(){
	cin>>n>>s; s=" "+s;
	for (int i=2; i<=n; i++){
		cin>>f[i]; vc[f[i]].push_back(i);
	}dfs(1);
	int ansx=0;
	for (int i=1; i<=n; i++){
		ansx^=i*ans[i];
	}cout<<ansx; return 0;
	
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值