P7834 [ONTAK2010] Peaks 加强版 题解

下面称 Kruscal 建树的时候的产生的点的权值为 “权值”,题目中给的点权称作 “贡献”。

  • 先是对于原图跑最小生成树的同时建立 Kruscal 重构树
  • 随后 dfs 遍历整棵树,维护好倍增数组以及给叶子节点(原图节点)编号,并且处理出每个非叶子节点 uu 为根的子树中包含的叶子节点的编号 最大 / 最小值。
  • 按照编号的顺序将叶子节点的 "贡献" 插入主席树中。
  • 查询的时候就倍增找到询问的点 uu 在树中最远的一个满足权值 \leq x≤x 的节点 pp(因为深度越浅,权值越大),它为根的子树里面任意一个点跟 uu 的 LCA 的权值一定会 \leq p≤p 的权值。
  • 求 pp 为根的子树中包含的叶子节点的 “贡献” 第 kk 大,因为编号连续,可以通过主席树区间查询。

时间复杂度:O(m \log n + n \log n)O(mlogn+nlogn),空间复杂度:O(n \log n)O(nlogn)

Code

#include <bits/stdc++.h>
using namespace std;
inline int read() {
    int x = 0, flag = 1;
    char ch = getchar();
    for( ; ch > '9' || ch < '0' ; ch = getchar()) if(ch == '-') flag = -1;
    for( ; ch >= '0' && ch <= '9' ; ch = getchar()) x = (x << 3) + (x << 1) + ch - '0';
    return x * flag;
}
const int MAXN = 2e5 + 50, MAXM = 5e5 + 50;
int n, m, f[MAXM + MAXN], tot, rt, start[MAXN], q, V[MAXN];
int fa[MAXN][20], L[MAXN], dfn_id[MAXN], R[MAXN], dep[MAXN], A[MAXN], now = 0;
int head, tail, Rt[MAXN], cnt = 0;
vector <int> Son[MAXN];
struct Edge {
    int from, to, w;
} e[MAXM << 1];
int find(int x) { return f[x] == x ? x : f[x] = find(f[x]); }
bool cmp(Edge a, Edge b) { return a.w < b.w; }
struct SegmentTree {
    int ls, rs, Sum;
} T[MAXN * 16];

inline void DFS(int x, int from) {
    fa[x][0] = from, dep[x] = dep[from] + 1;
    L[x] = 1e9 + 7, R[x] = 0;
    for(int i = 1 ; i <= log2(dep[x]) ; i ++)
        fa[x][i] = fa[fa[x][i - 1]][i - 1];
    if(!Son[x].size()) L[x] = R[x] = ++ now, dfn_id[now] = x;
    for(int i = 0 ; i < Son[x].size(); i ++) {
        int to = Son[x][i];
        DFS(to, x);
        L[x] = min(L[to], L[x]);
        R[x] = max(R[to], R[x]);
    }
    return ;
}
inline int insert(int x, int l, int r, int pos) {
    int cur = ++ cnt, mid = (l + r) >> 1;
    T[cur] = T[x], T[cur].Sum ++;
    if(l == r) return cur;
    if(pos <= mid) T[cur].ls = insert(T[x].ls, l, mid, pos);
    else T[cur].rs = insert(T[x].rs, mid + 1, r, pos);
    return cur;
}
inline int Getfa(int x, int s) {
    for(int i = log2(dep[x]) ; i >= 0 ; i --)
        if(fa[x][i] != 0 && V[fa[x][i]] <= s) x = fa[x][i];
    return x;
}
inline int GetAns(int u, int v, int l, int r, int k) {
    int S = T[T[v].rs].Sum - T[T[u].rs].Sum, mid = (l + r) >> 1;
    if(l == r) return l;
    if(S >= k) return GetAns(T[u].rs, T[v].rs, mid + 1, r, k);
    else return GetAns(T[u].ls, T[v].ls, l, mid, k - S);
}
signed main() {
    //freopen("out","w",stdout);
    n = read(), m = read(), q = read(), rt = n;
    for(int i = 1 ; i <= n ; i ++) A[i] = read(); 
    for(int i = 1 ; i <= m ; i ++) {
        int u = read(), v = read(), w = read();
        e[i] = (Edge) { u, v, w };
    }
    for(int i = 1 ; i <= n + m ; i ++) f[i] = i;
    sort(e + 1, e + 1 + m, cmp);
    for(int i = 1 ; i <= m ; i ++) {
        int u = find(e[i].from), v = find(e[i].to);
        if(u != v) {
            rt ++, f[u] = f[v] = f[rt] = rt;
            V[rt] = e[i].w;
            Son[rt].push_back(u), Son[rt].push_back(v);
        }
    } DFS(rt, 0);
    int Ans = 0;
    for(int i = 1 ; i <= n ; i ++) 
        Rt[i] = insert(Rt[i - 1], 1, 1e9, A[dfn_id[i]]);
    for(int i = 1 ; i <= q ; i ++) {
        int u = (read() ^ Ans) % n + 1, x = read() ^ Ans, k = (read() ^ Ans) % n + 1;
        u = Getfa(u, x);
        if(R[u] - L[u] + 1 < k) Ans = 0, printf("-1\n");
        else Ans = GetAns(Rt[L[u] - 1], Rt[R[u]], 1, 1e9, k), printf("%d\n", Ans);
    }
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值