[ONTAK2010] Peaks

[ONTAK2010] Peaks

题目大意:有\(n\)个不同高度的山峰,\(m\)条带权无向边,\(q\)个询问,询问从山峰\(a\)点开始只经过权值小于等于\(b\)的路径所能到达的山峰中第\(k\)高的山峰,如果无解输出\(-1\)

Solution

算法流程如下:

  1. 输入山峰高度,从低到高排序,用序号代替具体值来离散。

  2. 对于每一个山峰建立一颗权值线段树

  3. 输入\(m\)条边和\(q\)个询问,存在同一个数组中按照边权来排序从小到大排序,因为如果之前的边都满足不了此次询问,那么后面的边权更大,对于这次询问来说是无效的

  4. 遍历边和询问,

    1. 对于,如果指向的两个点还不在一个线段树里,那么就合并线段树(别忘记合并并查集),如果指向的两个点已经在一个线段树里了,就跳过,因为之前那次合并一定是最短的
    2. 对于询问,就是查找\(a\)点的权值线段树中的第\(k\)高,因为我们排了序,所以就消去了对于边权的限制
  5. 输出询问的答案

注意,询问是要标注\(id\)的.

Attention

  • 线段树合并的空间复杂度是\(n\cdot (log_2^n+1)\)

因为合并的过程并没有新开节点,一开始每个元素都是单独的一颗线段树,每个元素开的空间是一条树链 ,树链长度是 \(log_2^n+1\)的,最多把所有树链合并起来,所以空间复杂度是\(n\cdot (log_2^n+1)\)

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define sc(x) scanf("%d", &x)
#define pf(x) printf("%d\n",x)
using std::sort;

const int M = 1e6 + 10;
const int N = 1e5 + 10;

struct Hill{
	int hi, id;
	bool operator < (const Hill &cmp) const {
		return hi < cmp.hi;
	}
}h[N];

struct Aha{
	int a, b, c, f, id;
	bool operator < (const Aha &cmp) const {
		if(c == cmp.c) return f < cmp.f;
		else return c < cmp.c;
	}
}a[M];

int n, m, q, cnt;
int root[N], fa[N], sum[2000005], lson[2000005], rson[2000005], ans[M];

int find(int x){
	if(x == fa[x]) return x;
	return fa[x] = find(fa[x]);
}

void insert(int &cur, int l, int r, int pos){//int cur
	if(!cur) cur = ++cnt;
	sum[cur] ++;//说明这个子树上包含的山加一
	if(l == r) return;//到了底
	int mid = l + ((r - l) >> 1);
	if(pos <= mid)//说明应该在左子树
		insert(lson[cur], l, mid, pos);
	else 
		insert(rson[cur], mid + 1, r, pos);
}

int merge(int x, int y){
	if(!x) return y;
	if(!y) return x;
	sum[x] += sum[y];
	lson[x] = merge(lson[x], lson[y]);
	rson[x] = merge(rson[x], rson[y]);
	return x;
}

int query(int cur, int l, int r, int k){
	if(l == r) return l;
	int mid = l + ((r - l) >> 1);
	if(sum[lson[cur]] >= k)
		return query(lson[cur], l, mid, k);
	else
		return query(rson[cur], mid + 1, r, k - sum[lson[cur]]);
}

int main(){
	scanf("%d %d %d", &n, &m, &q);
	for(int i = 1; i <= n; ++i)
		sc(h[i].hi), h[i].id = i, fa[i] = i; 
		
	sort(h + 1, h + 1 + n);
	for(int i = 1; i <= n; ++i)
		insert(root[h[i].id], 1, n, i);
	for(int i = 1; i <= m; ++i){
		sc(a[i].a);
		sc(a[i].b);
		sc(a[i].c);
		a[i].f = 0;
	}
	for(int i = m + 1; i <= m + q; ++i){
		sc(a[i].a);
		sc(a[i].c);
		sc(a[i].b);//和上面不一样,排序的是不能超过的难度值
		a[i].f = 1;
		a[i].id = i;
	}
	sort(a + 1, a + m + q + 1);
	for(int i = 1, fx, fy; i <= m + q; ++i){
		if(!a[i].f){//如果是边
			fx = find(a[i].a);
			fy = find(a[i].b);

			if(fx == fy) continue;
			root[fx] = merge(root[fx], root[fy]);
			fa[fy] = fx;
		} else {
			fx = find(a[i].a);
			if(sum[root[fx]] < a[i].b){
				 ans[a[i].id] = -1;
				 continue;//
			}
			ans[a[i].id] = query(root[fx], 1, n, sum[root[fx]] - a[i].b + 1);//root[root[fx]]
		}
	}
	for(int i = m + 1; i <= m + q; ++i){
		if(ans[i] == -1){
			puts("-1");
			continue;
		}//
		pf(h[ans[i]].hi);//ans[i];
	}
	return 0;
}
posted @ 2018-09-03 16:59  LMSH7  阅读(134)  评论(0编辑  收藏  举报