BZOJ3545: [ONTAK2010]Peaks(线段树合并)

传送门
这题允许离线的话,就直接线段树合并就可以了。
这个线段树就是一棵值域线段树。枚举一维表示边权的大小,把边权小于枚举值的边的两端所在的线段树合并了,在线段树里查询第k大值就可以了。
如果是加强版的话,可以使用主席树合并来完成。

#include <cstdio>
#include <algorithm>
using namespace std;
#define MAXN 500005
int n, m, q, h[MAXN], ans[MAXN], rt[MAXN], fa[MAXN], cnt;
inline void GET(int&n) {
    char c; n = 0; do c = getchar(); while('0' > c || c > '9');
    while('0' <= c && c <= '9')n = n*10 + c-'0', c = getchar();
}
struct Edge {
    int u, v, w;
    inline void scan() { GET(u); GET(v); GET(w); }
    inline bool operator < (const Edge&r) const { return w < r.w; }
} e[MAXN];
struct Query {
    int v, x, k, id;
    inline void scan(int i) { GET(v); GET(x); GET(k); id = i; }
    inline bool operator < (const Query&r)const { return x < r.x; }
} p[MAXN];
int findroot(int x) { return x == fa[x] ? x : (fa[x] = findroot(fa[x])); }
struct Seg { int l, r, c; } t[100005*32];
#define MID ((l + r) >> 1)
void ins(int&x, int p, int l = 1, int r = 1e9) {
    if(!x) x = ++ cnt; ++ t[x].c;
    if(l == r) return;
    if(p <= MID) ins(t[x].l, p, l, MID);
    else ins(t[x].r, p, MID+1, r);
}
int mer(int x, int y, int l = 1, int r = 1e9) {
    if(!x || !y) return x + y;
    if(l == r) { t[x].c = t[x].c + t[y].c; return x; }
    t[x].l = mer(t[x].l, t[y].l, l, MID);
    t[x].r = mer(t[x].r, t[y].r,MID+1,r);
    t[x].c = t[t[x].l].c + t[t[x].r].c;
    return x;
}
int getkth(int x, int k, int l = 1, int r = 1e9) {
    if(l == r) return l;
    if(t[t[x].r].c >= k) return getkth(t[x].r, k, MID+1, r);
    return getkth(t[x].l, k-t[t[x].r].c, l, MID);
}
void Merge(int u, int v) {
    u = findroot(u); v = findroot(v);
    if(u == v) return;
    rt[u] = mer(rt[u], rt[v]);
    fa[v] = u;
}
int main() {
    GET(n); GET(m); GET(q);
    for(int i = 1; i <= n; ++ i) GET(h[i]);
    for(int i = 1; i <= n; ++ i) fa[i] = i;
    for(int i = 1; i <= n; ++ i) ins(rt[i], h[i]);
    for(int i = 1; i <= m; ++ i) e[i].scan();
    sort(e + 1, e + m + 1);
    for(int i = 1; i <= q; ++ i) p[i].scan(i);
    sort(p + 1, p + q + 1);
    for(int i = 1, j = 1; i <= q; ++ i) {
        while(j <= m && e[j].w <= p[i].x) Merge(e[j].u, e[j].v), j ++;
        int r = rt[findroot(p[i].v)];
        ans[p[i].id] = t[r].c >= p[i].k ? getkth(r, p[i].k) : -1;
    }
    for(int i = 1; i <= q; ++ i) printf("%d\n", ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值