题目链接:https://codeforces.com/contest/1150/problem/E
题意:现在有一个括号序列,括号序列表达的是一个先序遍历的树结构,左括号表示从当前节点向下遍历,右括号表示向上回溯。有 m m m个操作,每次操作交换两个括号的位置,保证交换之后括号的匹配同样合理。现在你需要输出当前括号序列表示的树直径。
解题心得:
假设将树用树链剖分标号,那么两个被标号的节点 a a a、 b b b,如果 a < b a<b a<b那么他们的 l c m lcm lcm一定在 a a a和 b b b之间,并且 l c m lcm lcm一定是 a a a和 b b b之间深度最小的那个节点。
这样一个树的直径就是遍历 a a a、 b b b, d e p t h [ a ] + d e p t h [ b ] − 2 ∗ d e p t h [ l c m ] depth[a] + depth[b] - 2*depth[lcm] depth[a]+depth[b]−2∗depth[lcm]的最大值。但是如果在一棵树上按照公式找直径时间复杂度是非常大的,并且每次交换括号之后树都会变形,所以不能将括号序列化成树来找直径。
其实仔细观察会发现我们只需要每个节点的深度就行了,而深度可以直接由括号序列得到,假设左括号是 1 1 1,右括号是 − 1 -1 −1,那么某段括号区间代表的相对深度就能直接得到。这样就可以直接使用线段树进行维护,需要维护的值比较多,包括每段区间的:
codeforces原生的题解:https://codeforces.com/blog/entry/66783
#include
using namespace std;
const int maxn = 2e5+100;
struct Node {
int depth, Min_depth, Max_depth, Max_lv, Max_rv, Max_lrv;
}node[maxn<<2];
int n, m;
char parenthesis[maxn];
void init() {
scanf("%d%d%s", &n, &m, parenthesis+1);
}
Node get_left_parenthesis() {
return Node{1, 0, 1, 0, 1, 1};
}
Node get_right_parenthesis() {
return Node{-1, -1, 0, 2, 1, 1};
}
Node shifted_left(int depth, int root) {
return Node{node[root].depth + depth, node[root].Min_depth+depth, node[root].Max_depth+depth,
node[root].Max_lv-depth, node[root].Max_rv-depth, node[root].Max_lrv};
}
void merge(int root) {
int chl = root<<1;
int chr = root<<1|1;
Node rhs_shifted = shifted_left(node[chl].depth, chr);
node[root].depth = rhs_shifted.depth;
node[root].Min_depth = min(node[chl].Min_depth, rhs_shifted.Min_depth);
node[root].Max_depth = max(node[chl].Max_depth, rhs_shifted.Max_depth);
node[root].Max_lv = max(node[chl].Max_lv, max(rhs_shifted.Max_lv, node[chl].Max_depth - 2*rhs_shifted.Min_depth));
node[root].Max_rv = max(node[chl].Max_rv, max(rhs_shifted.Max_rv, rhs_shifted.Max_depth - 2*node[chl].Min_depth));
node[root].Max_lrv = max(max(node[chl].Max_lrv, rhs_shifted.Max_lrv), max(node[chl].Max_lv + rhs_shifted.Max_depth,
rhs_shifted.Max_rv + node[chl].Max_depth));
}
void build_tree(int root, int l, int r) {
if(l == r) {
if(parenthesis[l] == '(') node[root] = get_left_parenthesis();
else node[root] = get_right_parenthesis();
return ;
}
int mid = l + r >> 1;
build_tree(root<<1, l, mid);
build_tree(root<<1|1, mid+1, r);
merge(root);
}
int query_max() {
return node[1].Max_lrv;
}
void change(int root, int l, int r, int find_pos, int change_pos) {
if(l == r) {
if(parenthesis[change_pos] == '(') node[root] = get_left_parenthesis();
else node[root] = get_right_parenthesis();
return ;
}
int mid = l + r >> 1;
if(find_pos <= mid) {
change(root<<1, l, mid, find_pos, change_pos);
} else {
change(root<<1|1, mid+1, r, find_pos, change_pos);
}
merge(root);
}
void change(int pos1, int pos2) {
change(1, 1, 2*n-2, pos1, pos2);
change(1, 1, 2*n-2, pos2, pos1);
}
int main() {
// freopen("1.in", "r", stdin);
init();
build_tree(1, 1, 2*n-2);
printf("%d\n", query_max());
while(m--) {
int pos1, pos2; scanf("%d%d",&pos1, &pos2);
change(pos1, pos2);
swap(parenthesis[pos1], parenthesis[pos2]);
printf("%d\n", query_max());
}
return 0;
}