「MdOI2020」 Treequery

数据结构大杂烩。。

我临结束一个半小时开始写的,没调出来,结束后十分钟才调出来。。

题解

前置知识:树的基本知识(dfs 序,欧拉序),ST 表,线段树,可持久化线段树(主席树)

我是两个 log,我不知道正解复杂度多少。。

考虑将 [ l , r ] [l,r] [l,r] 的点分成三部分,一部分在 p p p 的子树里,一部分不在,却与 p p p 同在 1 1 1 的一个子树里,一部分与 p p p 不在 1 1 1 的一个子树里。

如果 A A A B ∪ C B\cup C BC 两者之内都有点,答案就是 0 0 0

如果点都在 A A A 内,我们只需要知道 lca ( l ∼ r ) \text{lca}(l\sim r) lca(lr) 即可。

如果点都在 B ∪ C B\cup C BC 内,且只在 C C C 内,我们也只需要知道 lca ( l ∼ r ) \text{lca}(l\sim r) lca(lr) 即可。

接下来只需考虑最后一种情况:没有点在 A A A 中,有一些点在 B B B 中。

然后我们还是要分两种情况:

一种是这样的:

就是 1 到 p 的路径上伸出多条子树里面都有点,那么我们只需要找到那个红色位置(最低的那棵子树)即可。

一种是这样的:

此时我们不仅要知道红色位置,还需要知道 lca ( l ∼ r ) \text{lca}(l\sim r) lca(lr)

总而言之,我们需要知道怎么求最低的那棵子树,以及 lca ( l ∼ r ) \text{lca}(l\sim r) lca(lr)

首先考虑求 lca ( l ∼ r ) \text{lca}(l\sim r) lca(lr),这个就比较简单,我们使用线段树维护,在 pushup 的时候需要 O ( 1 ) O(1) O(1) 的求两个点的 lca,我们使用 ST 表维护欧拉序上的最小值即可。这一部分的时间复杂度是 O ( n log ⁡ n ) O(n\log n) O(nlogn)

然后考虑求最低的那棵子树,我们倍增(二分)往上跳,然后只需要知道某个点的子树内是否有 l ∼ r l\sim r lr 的子树,我们做出 dfs 序,建立以 dfs 序为下标的可持久化权值线段树,对 1 ∼ i 1\sim i 1i 的子树我们只需要将 dfn 1 ∼ i \text{dfn}_{1\sim i} dfn1i 都标为 1 即可。

我们发现我们也顺带获得了判断 4 种情况的方法。

时间复杂度 O ( n log ⁡ 2 n ) O(n\log^2 n) O(nlog2n)

代码

全场最慢 我都没 rush 出来,我人没了

常数巨大代码:

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
ll read() {
    ll ret = 0; char c = getchar();
    while (c < '0' || c > '9') c = getchar();
    while (c >= '0' && c <= '9') ret = ret * 10 + c - '0', c = getchar();
    return ret;
}

int N, Q;

const int MAXN = 200005;
ll dep[MAXN]; int fa[MAXN][21], dfn[MAXN], siz[MAXN], in[MAXN], rein[MAXN << 1], euler[MAXN << 1], dfncnt, eulercnt;

struct node { int v, next; ll k; } E[MAXN << 1]; int head[MAXN], Elen;
void add(int u, int v, ll k) { ++Elen, E[Elen].v = v, E[Elen].next = head[u], head[u] = Elen, E[Elen].k = k; }

void dfs(int u, int ff) {
    fa[u][0] = ff, dfn[u] = ++dfncnt, in[u] = ++eulercnt, rein[eulercnt] = u, euler[eulercnt] = u, siz[u] = 1;
    for (int i = 1; i <= 20; ++i) fa[u][i] = fa[fa[u][i - 1]][i - 1];
    for (int i = head[u]; i; i = E[i].next) {
        if (E[i].v != ff) {
            dep[E[i].v] = dep[u] + E[i].k, dfs(E[i].v, u);
            euler[++eulercnt] = u, siz[u] += siz[E[i].v];
        }
    }
}
int st[MAXN << 1][21];
void buildST() {
    for (int i = eulercnt; i >= 1; --i) {
        st[i][0] = in[euler[i]];
        for (int j = 1; i + (1 << j) - 1 <= eulercnt; ++j) {
            st[i][j] = min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
        }
    }
}
inline int lca(int u, int v) {
    if (u == v) return u;
    u = in[u], v = in[v];
    if (u > v) swap(u, v);
    int lg = log2(v - u + 1);
    return rein[min(st[u][lg], st[v - (1 << lg) + 1][lg])];
}

namespace T1 {
    int ans[MAXN << 2];
    #define mid ((l + r) >> 1)
    void build(int o, int l, int r) {
        if (l == r) ans[o] = l;
        else build(o << 1, l, mid), build(o << 1 | 1, mid + 1, r), ans[o] = lca(ans[o << 1], ans[o << 1 | 1]);
    }
    ll query(int o, int l, int r, int L, int R) {
        if (l == L && r == R) return ans[o];
        else {
            if (R <= mid) return query(o << 1, l, mid, L, R);
            else if (L > mid) return query(o << 1 | 1, mid + 1, r, L, R);
            else return lca(query(o << 1, l, mid, L, mid), query(o << 1 | 1, mid + 1, r, mid + 1, R));
        }
    }
};

int sum[MAXN << 5], rt[MAXN], lson[MAXN << 5], rson[MAXN << 5], Tlen;
#define mid ((l + r) >> 1)
void insert(int& o, int l, int r, int rt, int p) {
    o = ++Tlen;
    if (l == r) sum[o] = sum[rt] + 1;
    else {
        lson[o] = lson[rt], rson[o] = rson[rt];
        if (p <= mid) insert(lson[o], l, mid, lson[rt], p);
        else insert(rson[o], mid + 1, r, rson[rt], p);
        sum[o] = sum[lson[o]] + sum[rson[o]];
    }
}
int query(int o, int l, int r, int L, int R) {
    if (l == L && r == R) return sum[o];
    else {
        if (R <= mid) return query(lson[o], l, mid, L, R);
        else if (L > mid) return query(rson[o], mid + 1, r, L, R);
        else return query(lson[o], l, mid, L, mid) + query(rson[o], mid + 1, r, mid + 1, R);
    }
}
#undef mid

inline int Query(int p, int l, int r) { return query(rt[r], 1, N, dfn[p], dfn[p] + siz[p] - 1) - query(rt[l - 1], 1, N, dfn[p], dfn[p] + siz[p] - 1); }

ll lastans;
int main() {
    scanf("%d%d", &N, &Q); int i, u, v, p, l, r; ll k;
    for (i = 1; i < N; ++i) scanf("%d%d%lld", &u, &v, &k), add(u, v, k), add(v, u, k);
    dfs(1, 0);
    buildST();
    T1::build(1, 1, N);
    for (i = 1; i <= N; ++i) {
        insert(rt[i], 1, N, rt[i - 1], dfn[i]);
    }
    while (Q--) {
        scanf("%d%d%d", &p, &l, &r), p ^= lastans, l ^= lastans, r ^= lastans;
        k = Query(p, l, r);
        if (k == r - l + 1) lastans = dep[T1::query(1, 1, N, l, r)] - dep[p];
        else if (k != 0) lastans = 0;
        else {
            u = p;
            for (i = 20; i >= 0; --i) if (dep[fa[u][i]] > 0) u = fa[u][i];
            k = Query(u, l, r);
            if (k == 0) lastans = dep[p] + dep[T1::query(1, 1, N, l, r)];
            else {
                u = p;
                for (i = 20; i >= 0; --i) {
                    if (fa[u][i] && Query(fa[u][i], l, r) == 0) u = fa[u][i];
                }
                u = fa[u][0]; int lc = T1::query(1, 1, N, l, r);
                if (dep[lc] < dep[u]) lastans = dep[p] - dep[u];
                else lastans = dep[p] + dep[lc] - (dep[u] << 1);
            }
        }
        printf("%lld\n", lastans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值