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

3545: [ONTAK2010]Peaks

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 2421   Solved: 654
[ Submit][ Status][ Discuss]

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

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

Sample Input

10 11 4
1 2 3 4 5 6 7 8 9 10
1 4 4
2 5 3
9 8 2
7 8 10
7 1 4
6 7 1
6 4 8
2 1 5
10 8 10
3 4 7
3 4 6
1 5 2
1 5 6
1 5 8
8 9 2

Sample Output

6
1
-1
8


HINT

【数据范围】

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




把边按权值排序,并且把询问按困难值排序,这样便可按顺序加边,用指针指向第一个询问,每当边的困难值小于等于当前询问的时候,便操作当前询问。因为所有的边都是双向边,并且我们加边是按边的困难值从小到大加的,那么当前图中的所有边都是可以走的,当前的图就是一个个独立的点集,一个点集中的所有点都是可以互达的。对于询问i, 我们只要找到q[i].v处在的点集,找到点集中的第k大的值就可以了。

我们可以把初始时的所有点都当做一棵线段树,每一次加边,如果边的两端的点不在一个点集里,那么就可以把这两个点属于的点集合为一个点集,两个线段树也可以合为一个线段树,找一个点集中的第k大就可以在线段树中找第k大,就容易实现了。

至于如何合并两个线段树:

对于每棵线段树中的点都动态建,然后合并的时候,对于两个线段树中对应的点,p1和p2,如果p1和p2只有一个有左儿子,那么保留有的那个左儿子,右儿子也是同样操作,若p1和p2都有左儿子,那么继续合并son[p][0]和son[p1][0],右儿子同理,递归操作就可以合并两个线段树了。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 1000005
#define X 6
#define INF 1e9+7
using namespace std;
int n,m,Q,fa[N],sz,tr[N*X],son[N*X][2],rt[N],ans[N],h[N];
struct he{
    int a,b,c;
}b[N];
struct ha{
    int v,x,k,p;
}q[N];
bool cmp(he a ,he b){
    return a.c<b.c;
}
bool cmp1(ha a,ha b){
    return a.x<b.x;
}
int gf(int x){
    if(x==fa[x]) return x;
    return fa[x]=gf(fa[x]); 
}
void insert(int &p,int l,int r,int x){
    if(l>x||x>r) return ;
    if(!p) p=++sz;
    if(l==r) {tr[p]=1;return ;}
    int mid=(l+r)/2;
    if(x<=mid){insert(son[p][0],l,mid,x);}
    else {insert(son[p][1],mid+1,r,x);}
    tr[p]=tr[son[p][0]]+tr[son[p][1]]; 
}
void modify(int p,int p1,int l,int r){
    tr[p]+=tr[p1];
    if(l==r) return ;
    int mid=(l+r)/2;
    if(son[p][0]&&son[p1][0]) modify(son[p][0],son[p1][0],l,mid);
    else if(son[p1][0]) son[p][0]=son[p1][0];
    if(son[p][1]&&son[p1][1]) modify(son[p][1],son[p1][1],mid+1,r);
    else if(son[p1][1]) son[p][1]=son[p1][1];
}
void work(int x){
    int l=gf(b[x].a),r=gf(b[x].b);
    if(l==r) return ;
    modify(rt[r],rt[l],0,INF);
    fa[l]=r;
}
int find(int p,int l,int r,int k){
    if(l==r) return l;
    int mid=(l+r)/2;
    if(tr[son[p][0]]>=k) return find(son[p][0],l,mid,k);
    return find(son[p][1],mid+1,r,k-tr[son[p][0]]);
}
void get(int now){
    int x=gf(q[now].v);
    if(tr[rt[x]]<q[now].k) ans[q[now].p]=-1;
    else ans[q[now].p]=find(rt[x],0,INF,tr[rt[x]]-q[now].k+1);
}
int main(){
    scanf("%d%d%d",&n,&m,&Q);
    for(int i=1;i<=n;i++) {
        scanf("%d",&h[i]); 
        insert(rt[i],0,INF,h[i]);
    }
     
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&b[i].a,&b[i].b,&b[i].c);
    }
    sort(b+1,b+1+m,cmp);
    for(int i=1;i<=Q;i++){
        scanf("%d%d%d",&q[i].v,&q[i].x,&q[i].k),q[i].p=i;
    }
    sort(q+1,q+1+Q,cmp1);
    for(int i=1;i<=n;i++)
        fa[i]=i;
    int now=1,d=0;
    while(now<=Q&&q[now].x<b[1].c) get(now),now++;
    b[m+1].c=INF;
    for(int i=1;i<=m;i++){
        work(i);
        int tmp=b[i].c;
        while(i<m&&b[i+1].c==tmp)i++,work(i);
        while(now<=Q&&q[now].x>=b[i].c&&q[now].x<b[i+1].c) get(now),now++;
    }
    for(int i=1;i<=Q;i++)
        printf("%d\n",ans[i]);
}


猜你喜欢

转载自blog.csdn.net/bingoo0o0o/article/details/78712408