BZOJ3545: [ONTAK2010]Peaks

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。
Source
By Sbullet

题解:
排序+离散化+并查集+线段树合并,具体看代码。
不知为啥这题线段树合并这么慢。

#include<bits/stdc++.h>
using namespace std;
int read(){
    int x=0,f=1; char ch=getchar();
    while (ch<'0'||ch>'9'){if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar(); }
    return x*f;
}
struct Tre{
    int l,r,a;
}tree[100005*40];
struct Edg{
    int fro,poi,cost;
}e[1000005];
struct Que{
    int v,x,k,id;
}a[1000005];
int root[1000005],b[1000005],c[1000005],rt=0,fa[1000005],ans[1000005];
bool tmp2(Que a,Que b){
    return (a.x<b.x)||(a.x==b.x&&a.id<b.id);
}
int find(int x){
    if (x==fa[x]) return x;
    fa[x]=find(fa[x]); return fa[x];
}
void insert(int &u,int l,int r,int x){
    if (!u) u=++rt;
    tree[u].a++;
    if (l==r){return;}
    int mid=(l+r)>>1;
    if (x<=mid) insert(tree[u].l,l,mid,x);
    else insert(tree[u].r,mid+1,r,x);
}
int merge(int x,int y){
    if (!y) return x;
    if (!x) return y;
    if (!tree[x].l&&!tree[x].r){
    tree[x].a=tree[x].a+tree[y].a; return x;}
    tree[x].l=merge(tree[x].l,tree[y].l); 
    tree[x].r=merge(tree[x].r,tree[y].r);
    tree[x].a=tree[tree[x].l].a+tree[tree[x].r].a; 
    return x;
}
int query(int u,int l,int r,int k){
    int num=tree[tree[u].l].a;
    int mid=(l+r)>>1;
    if (l==r) return l;
    if (k<=num) return query(tree[u].l,l,mid,k);
    else query(tree[u].r,mid+1,r,k-num);
}
int main(){
    int n=read(),m=read(),q=read();
    for (int i=1;i<=n;i++){
        b[i]=read(); c[i]=b[i]; fa[i]=i; }
    sort(c+1,c+1+n);
    for (int i=1;i<=n;i++){
        b[i]=lower_bound(c+1,c+1+n,b[i])-c;
        insert(root[i],1,n,b[i]);
    }
    for (int i=1;i<=m;i++){
    a[i].id=0;  a[i].v=read(); a[i].k=read(); a[i].x=read();}
    for (int i=m+1;i<=m+q;i++){
        a[i].v=read(); a[i].x=read(); a[i].k=read(); a[i].id=i-m;
    }
    sort(a+1,a+1+q+m,tmp2); 
    for (int i=1;i<=m+q;i++){
        if (a[i].id==0){ 
        int x=a[i].v,y=a[i].k;
        int fx=find(x),fy=find(y);
        if (fx!=fy){
            fa[fy]=fx;
            root[fx]=merge(root[fx],root[fy]);
        }
        } else{
            int fx=find(a[i].v);
            if (tree[root[fx]].a<a[i].k) ans[a[i].id]=-1;
            else ans[a[i].id]=c[query(root[fx],1,n,tree[root[fx]].a-a[i].k+1)];
        }
    }
    for (int i=1;i<=q;i++)
    printf("%d\n",ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值