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 ABC,在 A 、 B 、 C A、B、C ABC中分别取出 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 AB中的所有点和根节点链上的节点打上标记, C C C中所有点的所有子树打上标记,当且仅当一个节点同时拥有 3 3 3个标记时该节点算入答案。树剖可以解决链和子树打标记,问题是怎么维护同时拥有 3 3 3个标记的节点。以下从群佬们那参考整合了 4 4 4种方法。

代码:

1、 1 、 2 、 4 1、2、4 124分别代表 A 、 B 、 C A、B、C ABC的标记,维护区间"或"和"与"的值。正常区间修改带懒标记,询问时暴力下传带一点优化,当区间"或"小于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(树剖)

2022杭电多校第二场 A.Static Query on Tree

2022杭电多校第二场 A.Static Query on Tree