CF343D Water Tree
题面
这是一道树剖的裸题
(第一次水紫题有点快乐)
这道题其实就是轻重链剖分的操作拿出来罢了
我们看到这道题区间修改,单点查询
不难想到线段树吧
然后我们可以想到轻重链剖分那道题
我们会发现操作\(1\)就是将\(dfn[x]\)~\(dfn[x]+siz[x]-1\)这段区间都赋值为\(1\)
这样的话我们就可以\(O(log)\)地维护了
然后我们考虑一下操作\(2\)我们发现,跳重链地过程其实就是不断向\(1\)移动的过程
所以我们在每次跳重链的时候,顺便在线段树上修改一下值就好了
操作\(3\)其实就是线段树单点查询
话不多说,直接放代码
#include<bits/stdc++.h>
const int N = 5e5 + 5;
using namespace std;
int n, m, head[N], cnt, dfn[N], hev[N], dep[N], siz[N], top[N], sum, fa[N];
struct node{
int u, v, nxt;
}edge[N << 1];
void add(int u, int v){
edge[++ cnt].u = u;
edge[cnt].v = v;
edge[cnt].nxt = head[u];
head[u] = cnt;
}
struct tree{
protected:
#define ls(o) (o << 1)
#define rs(o) (o << 1 | 1)
#define mid ((l + r) >> 1)
int tr[N << 2], laz[N << 2];
void up(int o){ tr[o] = tr[ls(o)] + tr[rs(o)];}
void down(int o, int l, int r){
laz[ls(o)] = laz[rs(o)] = laz[o];
tr[ls(o)] = laz[o] * (mid - l + 1);
tr[rs(o)] = laz[o] * (r - mid);
laz[o] = -1;
}
void build(int o, int l, int r){
if(l == r){ tr[o] = 0; return ;}
build(ls(o), l, mid); build(rs(o), mid + 1, r);
up(o);
}
public:
void change(int o, int l, int r, int L, int R, int val){
if(L <= l && r <= R){
tr[o] = val * (r - l + 1); laz[o] = val;
return ;
}
if(laz[o] != -1) down(o, l, r);
if(L <= mid) change(ls(o), l, mid, L, R, val);
if(R > mid) change(rs(o), mid + 1, r, L, R, val);
up(o);
}
int query(int o, int l, int r, int x){
if(l == r){ return laz[o];}
if(laz[o] != -1) down(o, l, r);
if(x <= mid) return query(ls(o), l, mid, x);
else return query(rs(o), mid + 1, r, x);
}
void cz(int x){ build(1, 1, x);}
}St_tree;
struct Powtree{
void Ps(int x){
siz[x] = 1;
for(int i = head[x]; i; i = edge[i].nxt){
int to = edge[i].v;
if(to == fa[x]) continue;
dep[to] = dep[x] + 1;
fa[to] = x;
Ps(to);
siz[x] += siz[to];
if(siz[to] > siz[hev[x]]) hev[x] = to;
}
}
void dfs(int x, int topp){
top[x] = topp;
dfn[x] = ++ sum;
if(hev[x]) dfs(hev[x], topp);
for(int i = head[x]; i; i = edge[i].nxt){
int to = edge[i].v;
if(to == fa[x] || to == hev[x]) continue;
dfs(to, to);
}
}
void cz(){
dep[1] = 1;
Ps(1);
dfs(1, 1);
}
}Pow_tree;
int read(){
int op = 0, opp = 1; char ch = getchar();
while(ch < '0' || ch > '9'){ if(ch == '-') opp = -1; ch = getchar();}
while(ch <= '9' && ch >= '0'){ op = (op << 1) + (op << 3) + (ch ^ 48); ch = getchar();}
return op * opp;
}
void Build_tree(){
Pow_tree.cz();
St_tree.cz(n);
}
void Jump(int x){
while(x){
St_tree.change(1, 1, n, dfn[top[x]], dfn[x], 0);
x = fa[top[x]];
}
}
void maiin(){
n = read();
for(int i = 1, x, y; i < n; i ++){
x = read(); y = read();
add(x, y); add(y, x);
}
Build_tree();
m = read();
for(int i = 1, x, y; i <= m; i ++){
x = read(); y = read();
if(x == 1) St_tree.change(1, 1, n, dfn[y], dfn[y] + siz[y] - 1, 1);
if(x == 2) Jump(y);
if(x == 3) printf("%d\n", St_tree.query(1, 1, n, dfn[y]));
}
}
int main(){
maiin();
return 0;
}
这道题有个地方很细节,就是在线段树里,没有标记的时候,\(laz\)会被标记为\(-1\),这样就会防止没有\(0\)的标记,却操作中下放了\(0\)的标记,导致了无故的\(WA\)