bzoj3545

线段树合并+离线+启发式合并

半年前这道题t成狗。。。

离线的做法比较好想,按照边的权值排序,询问的权值排序,然后枚举询问不断加边,加到上限后查找第k大值,这里平衡树,权值线段树都可以实现。

那么我们用权值线段树就行了, 并查集维护两点连通性,不连通的话就合并,并查集连接。

#include<bits/stdc++.h>
using namespace std;
const int N = 500010;
struct Query {
    int v, x, k, id;
    bool friend operator < (Query A, Query B)
    {
        return A.x < B.x;
    }
} q[N];
struct edge {
    int u, v, w;
    bool friend operator < (edge A, edge B)
    {
        return A.w < B.w;
    }
} e[N];
int n, m, Q;
int root[N], h[N], ans[N];
vector<int> vt;
map<int, int> mp;
inline int read()
{
    int x = 0, f = 1; char c = getchar();
    while(c < ‘0‘ || c > ‘9‘) { if(c == ‘-‘) f = -1; c = getchar(); }
    while(c >= ‘0‘ && c <= ‘9‘) { x = x * 10 + c - ‘0‘; c = getchar(); }
    return x * f;
}
namespace seg
{
    int cnt;
    int lc[N << 2], rc[N << 2], size[N << 2];
    void update(int l, int r, int &x, int pos)
    {
        x = ++cnt;
        ++size[x];
        if(l == r) return;
        int mid = (l + r) >> 1;
        if(pos <= mid) update(l, mid, lc[x], pos);
        else update(mid + 1, r, rc[x], pos);
    }
    int merge(int x, int y)
    {
        if(!x) return y;
        if(!y) return x;
        size[x] += size[y];
        lc[x] = merge(lc[x], lc[y]);
        rc[x] = merge(rc[x], rc[y]);
        return x;
    }
    int query(int l, int r, int x, int rank)
    {
        if(l == r) return l;
        int mid = (l + r) >> 1;
        if(size[rc[x]] >= rank) return query(mid + 1, r, rc[x], rank);
        else return query(l, mid, lc[x], rank - size[rc[x]]);
    }
} using namespace seg;
int main()
{
    n = read();
    m = read();
    Q = read();
    for(int i = 1; i <= n; ++i)
    {
        h[i] = read();
        vt.push_back(h[i]);
    }
    sort(vt.begin(), vt.end());
    vt.erase(unique(vt.begin(), vt.end()), vt.end());
    for(int i = 1; i <= n; ++i)
    {
        h[i] = lower_bound(vt.begin(), vt.end(), h[i]) - vt.begin() + 1;
        update(1, n, root[i], h[i]);
    }
    for(int i = 1; i <= m; ++i)
    {
        e[i].u = read();
        e[i].v = read();
        e[i].w = read();
    }
    sort(e + 1, e + m + 1);
    for(int i = 1; i <= Q; ++i)
    {
        q[i].v = read();
        q[i].x = read();
        q[i].k = read();
        q[i].id = i;
    }
    sort(q + 1, q + Q + 1);
    int j = 1;
    for(int i = 1; i <= Q; ++i)
    {
        while(e[j].w <= q[i].x && j <= m)
        {
            if(root[e[j].u] != root[e[j].v]) root[e[j].u] = root[e[j].v] = merge(root[e[j].u], root[e[j].v]);
            ++j;
        }
        if(size[root[q[i].v]] < q[i].k) ans[q[i].id] = -1;
        else ans[q[i].id] = query(1, n, root[q[i].v], q[i].k);
    }
    for(int i = 1; i <= Q; ++i) printf("%d\n", ans[i]);
    return 0;
}

时间: 2024-10-24 09:42:56

bzoj3545的相关文章

bzoj3545 [ONTAK2010]Peaks

3545: [ONTAK2010]Peaks Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 572  Solved: 164[Submit][Status] Description 在Bytemountains有N座山峰,每座山峰有他的高度h_i.有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走,现在有Q组询问,每组询问询问从点v开始只经过困难值小于等于x的路径所能到达的山峰中第k高的山峰,如果无解输出-1. I

[您有新的未分配科技点][BZOJ3545&amp;BZOJ3551]克鲁斯卡尔重构树

这次我们来搞一个很新奇的知识点:克鲁斯卡尔重构树.它也是一种图,是克鲁斯卡尔算法求最小生成树的升级版首先看下面一个问题:BZOJ3545 Peaks. 在Bytemountains有N座山峰,每座山峰有他的高度h_i.有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走. 现在有Q组询问,每组询问询问从点v开始只经过困难值小于等于x的路径所能到达的山峰中第k高的山峰,如果无解输出-1.N<=1e5,M,Q<=5*1e5 上面这个题没有要求在线,因此我们可以离线构造

【bzoj3545】[ONTAK2010]Peaks 线段树合并

[bzoj3545][ONTAK2010]Peaks 2014年8月26日3,1512 Description 在Bytemountains有N座山峰,每座山峰有他的高度h_i.有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走,现在有Q组询问,每组询问询问从点v开始只经过困难值小于等于x的路径所能到达的山峰中第k高的山峰,如果无解输出-1. Input 第一行三个数N,M,Q.第二行N个数,第i个数为h_i接下来M行,每行3个数a b c,表示从a到b有一条困难

bzoj3545 [ONTAK2010]Peaks、bzoj3551 [ONTAK2010]Peaks加强版

题目描述: bzoj3545,luogu bzoj3551 题解: 重构树+线段树合并. 可以算是板子了吧. 代码(强制在线): #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N = 100050; const int M = 5*N; template<typename T> inline void read(T&x) {

bzoj3545 &amp;&amp; bzoj3551 Peaks(离线版&amp;&amp;在线版)

题目给n点m边的无向图,有点权和边权 每次询问求点v在经过路径上的边都不超过w的情况下,能到达的第k大的点的权值 首先离线版比较容易想到,属于我现在能码出来的最难的码农题之一吧T T 这道题思路是这样的 1.对于边权的限制条件,可以先想到做一棵最小生成树 2.对于第k大这种询问,可以建权值线段树,但是山的高度太大到1e9,所以我们还要先离散化到1e6的水平才能用线段树 3.显然不能对每个询问w都建线段树,因此要用到主席树.具体做法是将询问和边权都排序(详情看代码),给每个点建一棵线段树,然后边建

[BZOJ3545] [ONTAK2010]Peaks(线段树合并 + 离散化)

传送门 由于困难值小于等于x这个很恶心,可以离线处理,将边权,和询问时的x排序. 每到一个询问的时候,将边权小于等于x的都合并起来再询问. .. 有重复元素的线段树合并的时间复杂度是nlog^2n #include <cstdio> #include <iostream> #include <algorithm> #define N 500001 int n, m, q, cnt, tot, size; int sum[N * 10], ls[N * 10], rs[N

[bzoj2733]永无乡&amp;&amp;[bzoj3545]Peaks

并不敢说完全会了线段树合并,只是至少知道原理写法了...还是太菜了,每天被大佬吊锤qwq 我看到的几道线段树合并都是权值线段树的合并.这个算法适用范围应该只是01线段树的. 这两道算入门题了吧... 发现粘题面没人看(自己都懒得看),以后粘链接加题意吧. 永无乡 给$n$个没有连边的带权点,动态加边,询问$u$所在连通块权值第$k$大的点是什么.$n \leq 1e5 , q\leq 3e5$ 离线永无乡?? 给定森林,点有点权有重复!,边有边权.询问$u$所在连通块,只能走边权小于$w$的边,

bzoj3545: [ONTAK2010]Peaks 主席树合并

排序以后,做并茶几+主席树合并维护,Orzstdcall,没想到权值线段树的合并竟然是O(nlogn)的...虽然他给我证明了一波,但是还是不是十分理解...听说是Cydiater给他讲的,Orz #include<bits/stdc++.h> using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getcha

bzoj3545 Peaks 线段树合并

离线乱搞... 也就是一个线段树合并没什么 #include<algorithm> #include<iostream> #include<cstring> #include<cstdio> using namespace std; int n,m,q,tot,cnt,num,h[100001],a[100001],ans[500001],fa[100001],root[100001]; struct edge{ int u,v,cost; bool ope