洛谷4092 [HEOI2016/TJOI2016]树

题目大意

给定一颗有根树,根为 $1$ ,有以下两种操作:

  1. 标记操作:对某个结点打上标记。(在最开始,只有结点 $1$ 有标记,其他结点均无标记,而且对于某个结点,可以打多次标记。)

  2. 询问操作:询问某个结点最近的一个打了标记的祖先。(这个结点本身也算自己的祖先)

$1 \leq n, q \leq 10^5 $

分析

我们注意到一个性质:祖先的$\texttt{dfs}$序比儿子的小。

于是,我们考虑这样一个做法:

  1. 最开始的时候,所有节点的值$a_i$为$1$
  2. 对于一次对节点$u$的标记,我们把$u$子树内的所有节点权值对$dfn_u$取$\texttt{max}$
  3. 对于查询操作,我们输出该节点的权值所对应的点。

那么,为什么这个做法是正确的呢?

回想到上面那个性质,当我们用$dfn_u$更新子树内节点时,其实就是把所有被标记的祖先对答案的影响覆盖了。

而由于我们是取$\max$,于是子树内如果某些节点已经被标记,则这些节点及他们的儿子的答案不会受影响。

所以这个做法是正确的。

又由于对区间取$\max$的时间复杂度是$\mathcal{O}(\log n)$级别的,于是我们的总复杂度是$\mathcal{O}(n \log n)$

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include <bits/stdc++.h>

#define R register
#define ll long long
#define sum(a, b, mod) (((a) + (b)) % mod)

const int MaxN = 2e5 + 10;

struct edge
{
int next, to;
};

struct node
{
int l, r;
int min, sec, tag;
};

edge e[MaxN << 1];
int n, m, cnt, dfscnt;
int head[MaxN], fa[MaxN], dep[MaxN], dfn[MaxN], pre[MaxN], size[MaxN];

struct SegmentTree
{
node t[MaxN << 2];
void pushup(int id)
{
int lc = id << 1, rc = id << 1 | 1;
if(t[lc].min == t[rc].min)
{
t[id].min = t[lc].min;
t[id].sec = std::min(t[lc].sec, t[rc].sec);
}
else if(t[lc].min < t[rc].min)
{
t[id].min = t[lc].min;
t[id].sec = std::min(t[lc].sec, t[rc].min);
}
else
{
t[id].min = t[rc].min;
t[id].sec = std::min(t[lc].min, t[rc].sec);
}
}
void build(int id, int l, int r)
{
t[id].l = l, t[id].r = r, t[id].tag = -1;
if(l == r) return (void) (t[id].min = 1, t[id].sec = 0x3f3f3f3f);
int mid = (l + r) >> 1;
build(id << 1, l, mid), build(id << 1 | 1, mid + 1, r);
}
void pushtag(int id, int val)
{
if(t[id].min >= val) return;
t[id].min = t[id].tag = val;
}
void pushdown(int id)
{
if(t[id].tag == -1) return;
pushtag(id << 1, t[id].tag), pushtag(id << 1 | 1, t[id].tag);
t[id].tag = -1;
}
void setval(int id, int l, int r, int val)
{
if(t[id].min >= val) return;
if(t[id].l > r || l > t[id].r) return;
if(l <= t[id].l && t[id].r <= r && t[id].sec > val)
return pushtag(id, val);
pushdown(id), setval(id << 1, l, r, val);
setval(id << 1 | 1, l, r, val), pushup(id);
}
int query(int id, int pos)
{
if(t[id].l > pos || t[id].r < pos)
return 0x3f3f3f3f;
if(t[id].l == t[id].r) return t[id].min;
pushdown(id);
return std::min(query(id << 1, pos), query(id << 1 | 1, pos));
}
}T;

void add_edge(int u, int v)
{
++cnt;
e[cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt;
}

void dfs(int u, int fa)
{
dep[u] = dep[fa] + 1, ::fa[u] = fa;
dfn[u] = ++dfscnt, pre[dfscnt] = u, size[u] = 1;
for(int i = head[u]; i; i = e[i].next)
{
int v = e[i].to;
if(v == fa) continue;
dfs(v, u), size[u] += size[v];
}
}

char get()
{
char ch = getchar();
while(!isalpha(ch))
ch = getchar();
return ch;
}

inline int read()
{
int x = 0;
char ch = getchar();
while(ch > '9' || ch < '0')
ch = getchar();
while(ch <= '9' && ch >= '0')
x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
return x;
}

int main()
{
n = read(), m = read();
for(int i = 1; i < n; i++)
{
int u = read(), v = read();
add_edge(u, v), add_edge(v, u);
}
dfs(1, 0), T.build(1, 1, n);
while(m--)
{
char op = get();
int u = read();
if(op == 'Q')
printf("%d\n", pre[T.query(1, dfn[u])]);
else T.setval(1, dfn[u], dfn[u] + size[u] - 1, dfn[u]);
}
return 0;
}
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×