【刷题笔记】日照集训 Day3
T1
你有一个字符串 s,其中有一些方括号和圆括号,以及小写字母 \(a\) 到 \(z\)。
从内到外,从左到右,去掉括号,每去掉一个方括号就把括号所包含的区间内的字符串反转(reverse),去掉一个圆括号就将括号所包含的区间内的所有字符 \(+1\)(\(a\) 到 \(b\),\(b\) 到 \(c\),\(c\) 到 \(d\) ... \(z\) 到 \(a\)),输出最后的字符串。
保证 s 去掉小写字母之后剩下的是一个合法的括号序列。
考虑将括号序列建成树,遇到一个 [ ] 就把遍历顺序反转,遇到一个 ( ) 就 \(+1\),递归实现。
#include<bits/stdc++.h>
#define N 1000010
#define fo(a, b, c) for(int b = a; b <= c; b++)
#define _fo(a, b, c) for(int b = a; b >= c; b--)
using namespace std;
char s[N];
int n, nxt[N], lst[N];
stack<int>st1, st2;
void dfs(int l, int r, int rev, int cnt){
if(rev){
fo(l, i, r){
if(s[i] == '(') dfs(i + 1, nxt[i] - 1, rev, cnt + 1);
if(s[i] == '[') dfs(i + 1, nxt[i] - 1, rev ^ 1, cnt);
if('a' <= s[i] && s[i] <= 'z'){
char ch = 'a' + (s[i] - 'a' + cnt) % 26;
cout << ch;
}
if(s[i] == '(' || s[i] == '[') i = nxt[i];
}
}
else{
_fo(r, i, l){
if(s[i] == ')') dfs(lst[i] + 1, i - 1, rev, cnt + 1);
if(s[i] == ']') dfs(lst[i] + 1, i - 1, rev ^ 1, cnt);
if('a' <= s[i] && s[i] <= 'z'){
char ch = 'a' + (s[i] - 'a' + cnt) % 26;
cout << ch;
}
if(s[i] == ')' || s[i] == ']') i = lst[i];
}
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> s + 1;
n = strlen(s + 1);
fo(1, i, n){
if(s[i] == '(') st1.push(i);
if(s[i] == '[') st2.push(i);
if(s[i] == ')'){
int p = st1.top(); st1.pop();
nxt[p] = i, lst[i] = p;
}
if(s[i] == ']'){
int p = st2.top(); st2.pop();
nxt[p] = i, lst[i] = p;
}
}
dfs(1, n, 1, 0);
return 0;
}
T2
起点 S 和终点 T 之间有三条长度不一的链。有 m 条额外
的边。现在有恰好三条不同的道路正在维修,维修期间不得通行,于是你需要求出,有多少种维修的方案满足你无法到学校。
首先注意到,选的边一定是 主路上的边。
其次,对于每一条连接同一条主路的额外边,会限制一段区间不能选,这个可以用 差分 快速处理出。
对于每一条连接不同主路的额外边,会使得两条主路上的边,必须要选在这条额外边的同一侧。进而,假设当前选了第一条主路上的第 \(i\) 边,则第二,三条主路上可选的边会分别构成一段区间。
因此我们可以枚举第一条主路上选了哪条边。设第二条路的可选区间为 \(l_2, r_2\),第三条路为 \(l_3,r_3\),可以发现这四个量都是单调不降的,因此可以用四个指针维护。同时,预处理出在第二条上选第 \(i\) 条边,第三条路径上可选边范围 \(l_i,r_i\),这样每次第二条路径上的 \(l_2,r_2\) 变化时,可以很好维护 \(l_3,r_3\) 的变化。

浙公网安备 33010602011771号