• [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

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

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

    Code

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #define sc(x) scanf("%d", &x)
    #define pf(x) printf("%d
    ",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;
    }
    
  • 相关阅读:
    指数
    汉诺塔问题
    只用递归和当前的栈实现栈的逆序
    让你996的不是你的老板,而是其他愿意996的人
    luke towan
    2020-9-3
    2020-9-3
    springboot注解
    2020-9-2
    20200827
  • 原文地址:https://www.cnblogs.com/LMSH7/p/9579439.html
Copyright © 2020-2023  润新知