[线段树 + 图论]CF343D Water Tree 题解
其实就是把一些简单操作换到了 dfn 上,然后需要动脑子想一下操作如何转化。
操作一是比较显然的。由于 dfn 是在 dfs 的基础上构成的,你记录一下每个点 \(i\) 的子树大小 \(siz_i\) 然后修改 \(dfn_i\) 到 \(dfn_i + siz_i - 1\) 范围内元素的值就可以了。
操作二不是很好想,需要一点技巧。标记路径用 dfn 不好做,我们可以只标记每次操作二的节点。然后每当你要对一个节点进行操作三的查询时,就看一下它的子树里有没有操作二标记的节点,这样就将路径问题转化成了子树问题,也可以用 dfn 解决了。
我们分别用两颗线段树来记录操作一和操作二。
操作三,对于查询的节点分别查找在操作一和操作二的线段树里的值就行。然而如果一个节点被操作一和操作二分别修改过,如何辨别这两者的先后呢?对于操作一,是权值的操作,我们只能再开一颗线段树记录每个节点最后被修改的时间;对于操作二,由于是标记节点,我们就可以在标记的时候直接将要标记的点改成当前的时间,然后在子树查询时直接查区间最大值。
因此,我们分别需要实现区间修改,单点查询的线段树和单点修改,区间查询最大值的线段树。
#include <bits/stdc++.h>
constexpr int N = 5e5 + 7;
int p , opt , tot , cnt , u , v , n , head[N] , dfn[N] , siz[N] , m;
using namespace std;
struct edge {
int t , n;
}e[N << 1];
inline void add(int f , int t) {
e[++cnt].t = t , e[cnt].n = head[f] , head[f] = cnt;
}
void dfs(int s , int fa) {
siz[s] = 1;
if(!dfn[s]) {
dfn[s] = ++ tot; //处理一下子树大小和dfn
}
for(int i = head[s]; i ; i = e[i].n) {
int to = e[i].t;
if(to != fa) {
dfs(to , s);
siz[s] += siz[to];
}
}
}
struct Segment_Tree1 { //区间修改单点查询的线段树
int change[N << 2];
void pushdown(int i) {
if(change[i]) {
change[i << 1] = change[i];
change[i << 1 | 1] = change[i];
change[i] = 0;
}
}
void modify(int i , int l , int r , int L , int R , int k) {
if(L <= l && r <= R) {
change[i] = k;
return;
}
pushdown(i);
int mid = l + r >> 1;
if(mid >= L) {
modify(i << 1 , l , mid , L , R , k);
}
if(mid < R) {
modify(i << 1 | 1 , mid + 1 , r , L , R , k);
}
}
int query(int i , int l , int r , int pos) {
if(l == r && l == pos) {
return change[i];
}
pushdown(i);
int mid = l + r >> 1;
if(pos <= mid) {
return query(i << 1 , l , mid , pos);
}
else {
return query(i << 1 | 1 , mid + 1 , r , pos);
}
}
}T1 , Time1;
struct Segment_Tree2 { //单点修改,区间查询最大值的线段树
int Max[N << 2];
void modify(int i , int l , int r , int pos , int k) {
if(l == r && l == pos) {
Max[i] = k; return;
}
int mid = l + r >> 1;
if(pos <= mid) {
modify(i << 1 , l , mid , pos , k);
}
else {
modify(i << 1 | 1 , mid + 1 , r , pos , k);
}
Max[i] = max(Max[i << 1] , Max[i << 1 | 1]);
}
int query(int i , int l , int r , int L , int R) {
if(L <= l && r <= R) {
return Max[i];
}
int res = 0 , mid = l + r >> 1;
if(mid >= L) {
res = max(res , query(i << 1 , l , mid , L , R));
}
if(mid < R) {
res = max(res , query(i << 1 | 1 , mid + 1 , r , L , R));
}
return res;
}
}T2;
int main() {
ios :: sync_with_stdio(0) , cin.tie(0) , cout.tie(0);
cin >> n;
for(int i = 1; i < n; ++i) {
cin >> u >> v;
add(u , v) , add(v , u);
}
dfs(1 , 0);
cin >> m;
for(register int i = 1; i <= m; ++i) {
cin >> opt >> p;
switch(opt) {
case 1 : {T1.modify(1 , 1 , n , dfn[p] , dfn[p] + siz[p] - 1 , 1); Time1.modify(1 , 1 , n , dfn[p] , dfn[p] + siz[p] - 1 , i); break;}
case 2 : {T2.modify(1 , 1 , n , dfn[p] , i); break;}
case 3 : {
int t1 = Time1.query(1 , 1 , n , dfn[p]);
int t2 = T2.query(1 , 1 , n , dfn[p] , dfn[p] + siz[p] - 1);
if(t1 <= t2) {
cout << 0 << '\n';
}
else if(t1 > t2){
cout << T1.query(1 , 1 , n , dfn[p]) << '\n';
}
break;
}
}
}
return 0;
}