【刷题笔记】日照集训 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\) 的变化。

posted @ 2025-08-12 11:49  GuoSN0410  阅读(7)  评论(0)    收藏  举报