首页 > 其他分享 >P4197 Peaks 题解

P4197 Peaks 题解

时间:2023-08-19 20:00:38浏览次数:33  
标签:10 le Peaks int 题解 fa disc P4197 include

P4197 Peaks 题解

题目描述

在 Bytemountains 有 \(n\) 座山峰,每座山峰有他的高度 \(h_i\)。有些山峰之间有双向道路相连,共 \(m\) 条路径,每条路径有一个困难值,这个值越大表示越难走。

现在有 \(q\) 组询问,每组询问询问从点 \(v\) 开始只经过困难值小于等于 \(x\) 的路径所能到达的山峰中第 \(k\) 高的山峰,如果无解输出 \(-1\)。
对于 \(100\%\) 的数据,\(n \le 10^5\),\(0 \le m,q \le 5\times 10^5\),\(h_i,c,x \le 10^9\)。

思路

由于有最大瓶颈路的限制,考虑 \(\text{Kruskal}\) 重构树,如果我们当前已经构建出了这张图的 重构树,观察发现,从点 \(v\) 开始只经过最大边权 \(\le x\) 的边等价于找到 \(v\) 最浅的虚点祖先 \(u\) 满足其点权 \(\le x\),那么 \(v\) 每次可以到达的点都只能是 \(u\) 点控制的点,也就是其子孙。

一个虚点的子孙在 \(\text{dfn}\) 序上一定是一段连续的区间,问题就转化为了区间第 \(k\) 大,用主席树维护即可。

时间复杂度:\(O(m\log m + n\log n + q\log n)\),这是在线算法,有一个双倍经验嘿嘿嘿。

#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
typedef long long ll;

const int N = 5e5 + 10;
int n, m, q, h[N];
struct qwq {
    int a, b, c;
    bool operator < (const qwq &W) const {
        return c < W.c;
    }
} e[N];
int fa[N], idx;
vector<int> g[N];
qwq p[N];
int find(int x) {
    return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void kruskal() {
    sort(e + 1, e + m + 1);
    idx = n;
    for(int i = 1; i <= m; i ++) {
        int x = find(e[i].a), y = find(e[i].b);
        if(x == y) continue;
        g[++ idx].push_back(x), g[idx].push_back(y), fa[idx] = idx;
        p[idx].c = e[i].c; 
        fa[x] = idx, fa[y] = idx;
    }
}
int f[N][23];
int pos[N], awa, dfn[N];
void dfs(int u) {
    if(g[u].size() == 0) pos[u] = ++ awa, dfn[awa] = u, p[u].a = p[u].b = awa;
    else p[u].a = 1e9;
    for(auto v : g[u]) {
        f[v][0] = u;
        for(int i = 1; i <= 22; i ++) f[v][i] = f[f[v][i - 1]][i - 1];
        dfs(v);
        p[u].a = min(p[v].a, p[u].a), p[u].b = max(p[u].b, p[v].b);
    }
}

int getfa(int u, int x) {
    for(int i = 22; i >= 0; i --)
        if(f[u][i] && p[f[u][i]].c <= x)
            u = f[u][i];
    return u;
}

int dat[N << 4], ls[N << 4], rs[N << 4], tot, root[N];
int update(int q, int l, int r, int x) {
    int p = ++ tot;
    int mid = l + r >> 1;
    
    dat[p] = dat[q], ls[p] = ls[q], rs[p] = rs[q];
    if(l == r && x == l) return dat[p] ++, p;
    if(x <= mid) ls[p] = update(ls[q], l, mid, x);
    else rs[p] = update(rs[q], mid + 1, r, x);
    dat[p] = dat[ls[p]] + dat[rs[p]];
    return p;
} 

int query(int p, int q, int l, int r, int k) {
    if(l == r) return l;
    int c = dat[rs[q]] - dat[rs[p]];
    int mid = l + r >> 1;
    if(k <= c) return query(rs[p], rs[q], mid + 1, r, k);
    else return query(ls[p], ls[q], l, mid, k - c);
} 

int disc[N], cnt; 
int get(int x) {
    return lower_bound(disc + 1, disc + cnt + 1, x) - disc;
}
signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> n >> m >> q;
    for(int i = 1; i <= n; i ++) cin >> h[i], fa[i] = i, disc[i] = h[i];
    sort(disc + 1, disc + n + 1);
    cnt = unique(disc + 1, disc + n + 1) - disc - 1;
    for(int i = 1; i <= m; i ++) cin >> e[i].a >> e[i].b >> e[i].c;
    kruskal();
    dfs(idx);
    
    for(int i = 1; i <= awa; i ++) 
        root[i] = update(root[i - 1], 1, cnt, get(h[dfn[i]]));
    
    int a, b, c, t;
    while(q --) {
        cin >> a >> b >> c;
        t = getfa(a, b);
        if(p[t].b - p[t].a + 1 < c)  {
            cout << -1 << '\n';
            continue;
        }
        cout << disc[query(root[p[t].a - 1], root[p[t].b], 1, cnt, c)] << '\n';
    }

    return 0;
}

标签:10,le,Peaks,int,题解,fa,disc,P4197,include
From: https://www.cnblogs.com/MoyouSayuki/p/17642977.html

相关文章

  • P9571 Horizon Blue 题解
    原题链接题目大意:\(有三个操作,分别为\)\(操作1加入一条直线\)\(操作2查询与一条直线相交但不重合的直线条数\)\(操作3删除所有与一条直线相交或重合的直线\)\(注意:后面两个操作的直线并不需要加入\)\(显然,两条直线相交不重合,当且仅当其k值不同\)\(所以可以把所有直线按k......
  • 寻宝 题解
    寻宝题目大意存在\(n\)个点和两种有向边:一类边分\(m\)组,每组的边权相同,从\([s_l,s_r]\)中的所有点连向\([t_l,t_r]\)中的所有点。二类边存在于任意两点\(i,j\)间,从\(i\)连向\(j\)的二类边的边权为\(|i-j|\timesa_i\)。求从点\(1\)到\(n\)的最短路......
  • P9425 [蓝桥杯 2023 国 B] AB 路线 题解
    ~~应该能过官方数据吧~~---回归正题。我开始想过更简单的深搜,但是我怕无法记忆化,所以选择了广搜。和普通的广搜不同,此题的队列要存$3$个维度,分别是$x$,$y$,$z$,分别表示横坐标、纵坐标、目前的步数模$2k$的值。此时我们可以把每$2k$步进行分组,前$k$步走在```A```的格子......
  • 洛谷P5410 【模板】扩展 KMP(Z 函数)题解
    题目链接P5410【模板】扩展KMP(Z函数)-洛谷|计算机科学教育新生态(luogu.com.cn) 分析先考虑z数组设nx[i]为字符串b与b以b[i]开头的后缀最长公共前缀设i为当前需要求的位置当前i+nx[i]-1的最大值所对应的i为q p为i对应的位置当i大于q+nx[q......
  • arc139,arc140,arc141题解
    ARC139A-DATrailingZeros憨的。BMakeN感觉没有那么naive。首先用\(1\)去更新一下后面两个决策的价值。然后有一个较为显然的东西是说\(\text{lcm}\)为周期,周期内应该贪心取最大的。周期外由于范围很小,可以直接枚举一种决策的次数,取最小值即可。复杂度是正确的。CO......
  • Luogu P9510 『STA - R3』高维立方体 题解
    题目传送门没见过这玩意,写个题解记下。题目大意周知斐波那契数列定义为:\[\operatorname{fib}(n)=\left\{\begin{aligned}1&&n\le2\\\operatorname{fib}(n-1)+\operatorname{fib}(n-2)&&n>2\end{aligned}\right.\]然后定义(\(n\le0\)函数值为\(0\))\[f(n)......
  • CF-1860C Game on Permutation题解
    题意:在一条数轴上,Alice可以跳到在你所在点前面且值比当前所在点小的点。每回合可以向任意符合要求的点跳一次。当轮到Alice的回合同时不存在符合要求的点,Alice就赢了。Alice可以选择一个点作为起始点,然后作为后手(赛时这里把我坑了)。问有多少个点是必胜的点。\(n\leq3\times10^5......
  • P6429 [COCI2008-2009#1] JEZ 题解
    题目传送门:Click。某蒟蒻看见这道题,想了足足一个晚上,过后茅塞顿开,故作此篇。感谢神犇的题解。看题目数据范围:\(1\leqr,c\leq10^6,1\leqk\leq10^{12}\),显然打暴力\(\mathcal{O}(rc)\)的时间复杂度是行不通的。必须做到近似于\(mathcal{O}(r)\)的时间复杂度。观察题......
  • [AGC004F] Namori 题解
    这里给出一种与其他题解完全不同的实现方式。思路发现图要么是一棵树,要么是一颗基环树。树我们首先考虑树如何操作。我们可以\(\text{dfs}\)这颗树。对于每个点维护一个\(w,h\),表示这个点想要变成白色\(w\)次,想要变成黑色\(h\)次。容易发现每个点最初状态都为\(w=0......
  • Mike and strings 题解
    题目传送门一道字符串题。由于\(n\)非常小,可以暴力枚举字符串。我们可以枚举其中一个字符串\(s_i\),然后让其他的字符串变成\(s_i\),最后记录一下次数,取一个最小值即可。在枚举第二个字符串的时候可以将它再复制一份自己到后面,然后可以用find函数来统计。当然,如果找不到,这......