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虚树区间合并树剖线段树维护标记等四种做法的主要内容,如果未能解决你的问题,请参考以下文章