【BZOJ3551】[ONTAK2010]Peaks加强版

【BZOJ3551】[ONTAK2010]Peaks加强版

题面

给你一个图,每次询问给定一个位置、长度和\(k\),问从这个点出发,只能经过不大于这个长度的边,到达的点中点权第\(k\)大的点权

图的规模:\(10^5\)

题解

考虑离线怎么做:

将所有询问存下来,按照边权排序

每次加边线段树合并查\(kth\)即可

然鹅:离线加边一时爽,强制在线火葬场。

我们考虑强制在线怎么做:

妈妈!!我会\(Kruskal\)重构树!!!

我们有\(Kruskal\)重构树(这里是\(MST\))的性质:深度越深,节点权值越小

可以二分一下最浅的权值小于等于\(k\)的点,那么直接在它子树内用主席树\(kth\)就行了

辣鸡bzoj,这题空间开得神他妈玄学,稍微变一点就会挂

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring> 
#include <cmath> 
#include <algorithm>
using namespace std; 
inline int gi() {
    register int data = 0, w = 1;
    register char ch = 0;
    while (!isdigit(ch) && ch != '-') ch = getchar(); 
    if (ch == '-') w = -1, ch = getchar();
    while (isdigit(ch)) data = 10 * data + ch - '0', ch = getchar();
    return w * data; 
}
const int MAX_N = 2e5 + 5;
const int MAX_LOG_N = 17; 
struct Node { int ls, rs, v; } t[MAX_N * 25];
int tot, rt[MAX_N << 1]; 
void insert(int &o, int p, int l, int r, int pos) { 
    o = ++tot, t[o] = t[p], ++t[o].v; 
    if (l == r) return ; 
    int mid = (l + r) >> 1; 
    if (pos <= mid) insert(t[o].ls, t[p].ls, l, mid, pos); 
    else insert(t[o].rs, t[p].rs, mid + 1, r, pos); 
} 
int query(int v, int u, int l, int r, int k) { 
    if (l == r) return l; 
    int mid = (l + r) >> 1, res = t[t[u].ls].v - t[t[v].ls].v; 
    if (res >= k) return query(t[v].ls, t[u].ls, l, mid, k); 
    else return query(t[v].rs, t[u].rs, mid + 1, r, k - res); 
} 
struct Graph { int to, next; } e[MAX_N << 1]; int fir[MAX_N], e_cnt;
void clearGraph() { memset(fir, -1, sizeof(fir)); e_cnt = 0; } 
void Add_Edge(int u, int v) { e[e_cnt] = (Graph){v, fir[u]}, fir[u] = e_cnt++; } 
struct Edge { int u, v, w; } a[MAX_N << 2]; 
bool operator < (const Edge &l, const Edge &r) { return l.w < r.w; } 
int N, M, Q, h[MAX_N]; 
int pa[MAX_N], val[MAX_N]; 
int getf(int x) { return (x == pa[x]) ? x : (pa[x] = getf(pa[x])); } 
int fa[MAX_N][MAX_LOG_N], mx[MAX_N][MAX_LOG_N], p[MAX_N << 1], dep[MAX_N], top; 
int st[MAX_N], ed[MAX_N]; 
bool vis[MAX_N]; 
void dfs(int x) {
    vis[x] = 1, p[++top] = x; 
    for (int i = 1; i <= 16; i++) 
        if (dep[x] >= (1 << i)) { 
            fa[x][i] = fa[fa[x][i - 1]][i - 1]; 
            mx[x][i] = max(mx[x][i - 1], mx[fa[x][i - 1]][i - 1]); 
        }
        else break; 
    
    for (int i = fir[x]; ~i; i = e[i].next) { 
        int v = e[i].to; 
        dep[v] = dep[x] + 1; 
        mx[v][0] = val[x]; 
        fa[v][0] = x; 
        dfs(v); 
    } 
    if (x > N) p[++top] = x; 
} 
void build() { 
    int ext = N; 
    sort(&a[1], &a[M + 1]); 
    for (int i = 1; i <= 2 * N; i++) pa[i] = i; 
    for (int i = 1; i <= M; i++) { 
        int x = getf(a[i].u), y = getf(a[i].v); 
        if (x != y) { 
            ext++; 
            pa[x] = pa[y] = ext; 
            val[ext] = a[i].w;
            Add_Edge(ext, y); 
            Add_Edge(ext, x); 
            if (ext == 2 * N - 1) break; 
        } 
    } 
    for (int i = 1; i <= N; i++) if (!vis[i]) dfs(getf(i)); 
    for (int i = 1; i <= top; i++) { 
        int x = p[i];
        if (x <= N) insert(rt[i], rt[i - 1], 1, N, h[x]); 
        else {
            rt[i] = rt[i - 1]; 
            if (st[x] == 0) st[x] = i;
            else ed[x] = i; 
        } 
    } 
} 
int find(int x, int v) { 
    for (int i = 17; i >= 0; i--) 
        if (dep[x] >= (1 << i) && mx[x][i] <= v) x = fa[x][i]; 
    return x; 
}
int X[MAX_N]; 
int main () { 
    clearGraph(); 
    N = gi(), M = gi(), Q = gi();
    for (int i = 1; i <= N; i++) X[i] = h[i] = gi(); 
    for (int i = 1; i <= M; i++) {
        int u = gi(), v = gi(), w = gi(); 
        a[i] = (Edge){u, v, w}; 
    } 
    sort(&X[1], &X[N + 1]); 
    for (int i = 1; i <= N; i++) h[i] = lower_bound(&X[1], &X[N + 1], h[i]) - X; 
    build(); int ans = 0; 
    while (Q--) {
        int v = gi(), x = gi(), k = gi();
        if (ans != -1) v ^= ans, x ^= ans, k ^= ans; 
        x = find(v, x); 
        int l = st[x], r = ed[x]; 
        if (t[rt[r]].v - t[rt[l - 1]].v < k) ans = -1; 
        else ans = X[query(rt[l], rt[r], 1, N, t[rt[r]].v - t[rt[l - 1]].v - k + 1)]; 
        printf("%d\n", ans); 
    } 
    return 0; 
} 

转载于:https://www.cnblogs.com/heyujun/p/10334460.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值