2022杭电多校第二场 1001 Static Query on Tree虚树区间合并树剖线段树维护标记等四种做法
Posted 灀龗䯦縷
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2022杭电多校第二场 1001 Static Query on Tree虚树区间合并树剖线段树维护标记等四种做法相关的知识,希望对你有一定的参考价值。
1001 Static Query on Tree
题意:
给定一棵 n n n个节点的树,每个节点只能向根节点走。 q q q个询问,每次给定三个集合 A 、 B 、 C A、B、C A、B、C,在 A 、 B 、 C A、B、C A、B、C中分别取出 x , y , z x,y,z x,y,z,问 x , y x,y x,y往最终在 z z z汇合, x , y x,y x,y都经过的点有多少个。
思路:
A 、 B A、B A、B中的所有点和根节点链上的节点打上标记, C C C中所有点的所有子树打上标记,当且仅当一个节点同时拥有 3 3 3个标记时该节点算入答案。树剖可以解决链和子树打标记,问题是怎么维护同时拥有 3 3 3个标记的节点。以下从群佬们那参考整合了 4 4 4种方法。
代码:
1、 1 、 2 、 4 1、2、4 1、2、4分别代表 A 、 B 、 C A、B、C A、B、C的标记,维护区间"或"和"与"的值。正常区间修改带懒标记,询问时暴力下传带一点优化,当区间"或"小于7时说明子树区间已经不可能对答案有贡献,不继续向下递归;当区间"与"等于7时说明子树区间全部满足,直接加上区间长度结束递归。感觉可以被卡掉。
#include <bits/stdc++.h>
#define int long long
#define x first
#define y second
#define ls u << 1
#define rs u << 1 | 1
#define endl '\\n'
#define pb push_back
#define all(x) x.begin(), x.end()
using namespace std;
const int N = 200010, M = 2 * N, _ = 0, P = 13331;
int k, t, q, n, m;
int h[N], e[M], ne[M], idx;
int depth[N], si[N], p[N], son[N];
int top[N];
int id[N], cnt;
struct Tree
int l, r;
int v1, v2;
int laz;
int clr;
tr[N << 2];
void add(int a, int b)
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
void dfs1(int u, int father, int dep)
p[u] = father, si[u] = 1, depth[u] = dep;
for (int i = h[u]; ~i; i = ne[i])
int j = e[i];
if (j == father)
continue;
dfs1(j, u, dep + 1);
si[u] += si[j];
if (si[son[u]] < si[j])
son[u] = j;
void dfs2(int u, int t)
id[u] = ++cnt;
top[u] = t;
if (!son[u])
return;
dfs2(son[u], t);
for (int i = h[u]; ~i; i = ne[i])
int j = e[i];
if (j == son[u] || j == p[u])
continue;
dfs2(j, j);
void pushup(int u)
tr[u].v1 = tr[ls].v1 & tr[rs].v1;
tr[u].v2 = tr[ls].v2 | tr[rs].v2;
void pushdown(int u)
if (tr[u].clr)
tr[ls].clr = tr[rs].clr = tr[u].clr;
tr[ls].v1 = tr[rs].v1 = 0;
tr[ls].v2 = tr[rs].v2 = 0;
tr[ls].laz = tr[rs].laz = 0;
tr[u].clr = 0;
if (tr[u].laz)
tr[ls].v1 |= tr[u].laz;
tr[rs].v1 |= tr[u].laz;
tr[ls].v2 |= tr[u].laz;
tr[rs].v2 |= tr[u].laz;
tr[ls].laz |= tr[u].laz;
tr[rs].laz |= tr[u].laz;
tr[u].laz = 0;
void build(int u, int l, int r)
tr[u] = l, r, 0, 0, 0, 0;
if (l == r)
return;
int mixd = l + r >> 1;
build(ls, l, mixd);
build(rs, mixd + 1, r);
void update(int u, int l, int r, int ty)
if (l <= tr[u].l && tr[u].r <= r)
tr[u].v1 |= ty;
tr[u].v2 |= ty;
tr[u].laz |= ty;
return;
pushdown(u);
int mixd = tr[u].l + tr[u].r >> 1;
if (l <= mixd)
update(ls, l, r, ty);
if (r > mixd)
update(rs, l, r, ty);
pushup(u);
int query(int u, int l, int r)
if (l <= tr[u].l && r >= tr[u].r)
if (tr[u].v1 >= 7) // v1>=7代表该子树中所有节点都满足条件,直接返回区间大小
return tr[u].r - tr[u].l + 1;
if (tr[u].l == tr[u].r)
return 0;
pushdown(u);
int mixd = tr[u].l + tr[u].r >> 1;
int res = 0;
if (l <= mixd && tr[ls].v2 >= 7) // v2>=7代表该子树有可能还有满足条件的,否则不进入子树递归,不加这个优化会T
res += query(ls, l, r);
if (r > mixd && tr[rs].v2 >= 7)
res += query(rs, l, r);
return res;
void update_path(int u, int v, int ty)
while (top[u] != top[v])
if (depth[top[u]] < depth[top[v]])
swap(u, v);
update(1, id[top[u]], id[u], ty);
u = p[top[u]];
if (depth[u] < depth[v])
swap(u, v);
update(1, id[v], id[u], ty);
void update_tree(int u, int ty)
update(1, id[u], id[u] + si[u] - 1, ty);
void solve()
cin >> n >> m;
memset(h, -1, sizeof(int) * (n + 10));
idx = 0;
for (int i = 2; i <= n; i++)
int a;
cin >> a;
add(a, i);
add(i, a);
dfs1(1, -1, 1);
dfs2(1, 1);
build(1, 1, n);
while (m--)
int a, b, c, x;
cin >> a >> b >> c;
for (int i = 1; i <= a; i++) //更新A集合中的点和根节点的链
cin >> x;
update_path(1, x, 1);
for (int i = 1; i <= b; i++) //更新B集合中的点和根节点的链
cin >> x;
update_path(1, x, 2);
for (int i = 1; i <= c; i++) //更新C集合中的点的子树
cin >> x;
update_tree(x, 4);
cout << query(1, 1, n) << endl; //查询整颗树中满足三个标记都打上了的节点数量
//清空根节点信息,靠pushdown往下清空线段树信息
tr[1].clr = 1;
tr[1].v1 = 0;
tr[1].v2 = 0;
tr[1].laz = 0;
signed main()
ios::sync_with_stdio(false), cin.tie(nullptr);
int T;
cin >> T;
// T=1;
while (T--)
solve();
return (0 ^ _ ^ 0);
2、同时维护 [ 0 , 7 ] [0,7] [0,7]之间的所有值,原始 a n s [ 0 ] ans[0] ans[0]等于区间长度,修改时将上一个状态的所有数量加到下一个状态里。比如区间加 1 1 1则将 a n s [ 0 , 2 , 4 , 7 ] ans[0,2,4,7] ans[0,2,4,7]的数量分别添加到 a n s [ 1 , 3 , 5 , 7 ] ans[1,3,5,7] ans[1,3,5,7]里,并清空 a n s [ 0 , 2 , 4 , 7 ] ans[0,2,4,7] ans[0,2,4,7]的数量。最后询问即 t r [ 1 ] . a n s [ 7 ] tr[1].ans[7] tr[1].ans[7]。
#include <bits/stdc++.h>
#define int long long
#define x first
#define y second
#define ls u << 1
#define rs u << 1 | 1
#define endl '\\n'
#define pb push_back
#define all(x) x.begin(), x.end()
using namespace std;
const int N = 200010, M = 2 * N, _ = 0, P = 13331;
int k, t, q, n, m;
int h[N], e[M], ne[M], idx;
int depth[N], si[N], p[N], son[N];
int<以上是关于2022杭电多校第二场 1001 Static Query on Tree虚树区间合并树剖线段树维护标记等四种做法的主要内容,如果未能解决你的问题,请参考以下文章
2022杭电多校第二场 1001 Static Query on Tree虚树区间合并树剖线段树维护标记等四种做法
2022杭电多校第二场 A.Static Query on Tree(树剖)
2022杭电多校第二场 A.Static Query on Tree(树剖)
2022杭电多校第二场 A.Static Query on Tree(树剖)