大家好,我是刘蕴熹,这是 本蒟蒻 第一次做提高组的T2。
好难呀!! 我真正感受到了T2的 威力 。
话说回来,大家请看阶梯得分:
第一阶梯:20+分
反正是 ,怎么搞都行,没什么难度,堪比红题。
这里介绍一种思路,可以先求出每个点所表示的括号串,然后暴力枚举子串左端点,然后扫一遍右端点,用一个变量 cnt 判断即可:
- 如果是 ‘)’,则 cnt--(如果cnt为负,则这个字符串不行,后面的都不行)。
- 否则,cnt++。
- 若操作完毕,cnt==0,则是合法的括号串。
代码详见第二阶梯(很类似)。
第二阶梯:50-分
其实也是 ,很玄学,其实加了个特判,何为特判?
实际上我们就可以发现,前面的第一条如过触发错误(为负),则我们跳出(其实它和前面的代码一样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分
本蒟蒻存有幻想,能不能 查询?
大胆猜测,小心求证
接下来我们可以开始求解了(先以链入手):
举个例子:
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;
}