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

1A还行

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有一条困难值为c的双向路径。
接下来Q行,每行三个数v x k,表示一组询问。

Output

对于每组询问,输出一个整数表示答案。

HINT

【数据范围】

N<=10^5, M,Q<=5*10^5,h_i,c,x<=10^9。


题目分析

题目所求的是“小于等于x的边”所成连通块中的第k大,这里就会自然想到处理这一类连通块问题的策略:从小到大加边维护连通块,并在这个过程中离线处理查询。

现在要维护的连通块信息是无序的集合,于是第一反应就是用set合并。但是显而易见的是,set不能处理第k大问题(话说暑假做“不等式组”那题时候第一反应就是用multiset处理,但是当时被查询key和第k大困扰了很久)。有一种常见的方法是采用权值线段树实现set的功能,这样一来就可以处理一些基础的查询问题。

处理完了连通块和维护的操作,接下去的问题就是合并。权值线段树本质上还是线段树,所以使用线段树合并的套路就可以保证这一部分的复杂度。

感觉是比较套路和数据结构的题,好像没什么营养……

  1 #include<bits/stdc++.h>
  2 const int maxn = 100035;
  3 const int maxq = 500035;
  4 const int maxm = 1000035;
  5 const int maxNode = 4000035;
  6
  7 struct node
  8 {
  9     int l,r,val;
 10 }a[maxNode];
 11 struct QRs
 12 {
 13     int v,x,k,id;
 14     bool operator < (QRs a) const
 15     {
 16         return x < a.x;
 17     }
 18 }qr[maxq];
 19 struct Edge
 20 {
 21     int u,v,val;
 22     Edge(int a=0, int b=0, int c=0):u(a),v(b),val(c) {}
 23     bool operator < (Edge a) const
 24     {
 25         return val < a.val;
 26     }
 27 }edges[maxm];
 28 int n,m,q,dal,tot,ans[maxq];
 29 int fat[maxn],h[maxn],cnt[maxn],rt[maxn];
 30
 31 int read()
 32 {
 33     char ch = getchar();
 34     int num = 0, fl = 1;
 35     for (; !isdigit(ch); ch=getchar())
 36         if (ch==‘-‘) fl = -1;
 37     for (; isdigit(ch); ch=getchar())
 38         num = (num<<1)+(num<<3)+ch-48;
 39     return num*fl;
 40 }
 41 int find(int x){return x==fat[x]?x:fat[x]=find(fat[x]);}
 42 int query(int rt, int l, int r, int c)
 43 {
 44     if (l==r) return l;
 45     int mid = (l+r)>>1;
 46     if (c <= a[a[rt].l].val)
 47         return query(a[rt].l, l, mid, c);
 48     return query(a[rt].r, mid+1, r, c-a[a[rt].l].val);
 49 }
 50 int queryPos(int v, int k)
 51 {
 52     int anc = find(v);
 53     if (a[rt[anc]].val < k) return -1;
 54     return cnt[query(rt[anc], 1, cnt[0], a[rt[anc]].val-k+1)];
 55 }
 56 void update(int &rt, int l, int r, int c)
 57 {
 58     if (!rt) rt = ++tot;
 59     ++a[rt].val;
 60     if (l==r) return;
 61     int mid = (l+r)>>1;
 62     if (c <= mid) update(a[rt].l, l, mid, c);
 63     else update(a[rt].r, mid+1, r, c);
 64 }
 65 void merge(int &u, int v)
 66 {
 67     if (u*v==0){
 68         u = u?u:v;
 69         return;
 70     }
 71     a[u].val += a[v].val;
 72     merge(a[u].l, a[v].l);
 73     merge(a[u].r, a[v].r);
 74 }
 75 int main()
 76 {
 77     n = read(), m = read(), q = read();
 78     for (int i=1; i<=n; i++) h[i] = cnt[i] = read(), fat[i] = i;
 79     for (int i=1; i<=m; i++)
 80         edges[i].u = read(), edges[i].v = read(), edges[i].val = read();
 81     for (int i=1; i<=q; i++)
 82         qr[i].v = read(), qr[i].x = read(), qr[i].k = read(), qr[i].id = i;
 83     std::sort(edges+1, edges+m+1);
 84     std::sort(cnt+1, cnt+n+1);
 85     std::sort(qr+1, qr+q+1);
 86     cnt[0] = std::unique(cnt+1, cnt+n+1)-cnt-1;
 87     for (int i=1; i<=n; i++)
 88     {
 89         h[i] = std::lower_bound(cnt+1, cnt+cnt[0]+1, h[i])-cnt;
 90         update(rt[i], 1, cnt[0], h[i]);
 91     }
 92     dal = 1;
 93     for (int i=1; i<=m; i++)
 94     {
 95         int fu = find(edges[i].u), fv = find(edges[i].v);
 96         if (fu!=fv){
 97             for (; dal<=q&&qr[dal].x < edges[i].val; ++dal)
 98                 ans[qr[dal].id] = queryPos(qr[dal].v, qr[dal].k);
 99             fat[fu] = fv, merge(rt[fv], rt[fu]);
100         }
101     }
102     for (int i=dal; i<=q; i++)
103         ans[qr[i].id] = queryPos(qr[i].v, qr[i].k);
104     for (int i=1; i<=q; i++) printf("%d\n",ans[i]);
105     return 0;
106 }

END

原文地址:https://www.cnblogs.com/antiquality/p/10290425.html

时间: 2024-11-10 11:35:04

【线段树合并】bzoj3545: [ONTAK2010]Peaks的相关文章

【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(线段树合并 + 离散化)

传送门 由于困难值小于等于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

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

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) {

【BZOJ4399】魔法少女LJJ 线段树合并

[BZOJ4399]魔法少女LJJ Description 在森林中见过会动的树,在沙漠中见过会动的仙人掌过后,魔法少女LJJ已经觉得自己见过世界上的所有稀奇古怪的事情了LJJ感叹道“这里真是个迷人的绿色世界,空气清新.淡雅,到处散发着醉人的奶浆味:小猴在枝头悠来荡去,好不自在:各式各样的鲜花争相开放,各种树枝的枝头挂满沉甸甸的野果:鸟儿的歌声婉转动听,小河里飘着落下的花瓣真是人间仙境”SHY觉得LJJ还是太naive,一天,SHY带着自己心爱的图找到LJJ,对LJJ说:“既然你已经见识过动态树

【BZOJ2733】永无乡[splay启发式合并or线段树合并]

题目大意:给你一些点,修改是在在两个点之间连一条无向边,查询时求某个点能走到的点中重要度第k大的点.题目中给定的是每个节点的排名,所以实际上是求第k小:题目求的是编号,不是重要度的排名.我一开始差点被这坑了. 网址:http://www.lydsy.com/JudgeOnline/problem.php?id=2733 这道题似乎挺经典的(至少我看许多神犇很早就做了这道题).这道题有两种写法:并查集+(splay启发式合并or线段树合并).我写的是线段树合并,因为--splay不会打+懒得学.

BZOJ 4756 线段树合并(线段树)

思路: 1.最裸的线段树合并 2. 我们可以观察到子树求一个东西 那我们直接DFS序好了 入队的时候统计一下有多少比他大的 出的时候统计一下 减一下 搞定~ 线段树合并代码: //By SiriusRen #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N=100050; int n,col[N],cpy[N],tree[N*100],lso

bzoj2733: [HNOI2012]永无乡(splay+启发式合并/线段树合并)

这题之前写过线段树合并,今天复习Splay的时候想起这题,打算写一次Splay+启发式合并. 好爽!!! 写了长长的代码(其实也不长),只凭着下午的一点记忆(没背板子...),调了好久好久,过了样例,submit,1A! 哇真的舒服 调试输出懒得删了QwQ #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<queue> #include

[XJOI NOI2015模拟题13] C 白黑树 【线段树合并】

题目链接:XJOI - NOI2015-13 - C 题目分析 使用神奇的线段树合并在 O(nlogn) 的时间复杂度内解决这道题目. 对树上的每个点都建立一棵线段树,key是时间(即第几次操作),动态开点. 线段树的节点维护两个值,一个是这段时间内的 1 操作个数,另一个是这段时间内变化的黑色节点权值和. 在处理所有操作的时候,每棵线段树都是仅代表树上的一个点,因此线段树的每个节点维护的就是这段时间内以这个点为 a 的 1 操作个数和这段时间内这个点的黑色节点权值和(这个点 x 由黑变白就 -