[bzoj3545+3551][ONTAK2010]Peaks&&加强版(离线+线段树合并)||(kruskal重构树&&dfs序+主席树)

传送门
没权限号可以来这里交,老规矩,不准说。

题面

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。


题解

首先,这个题有一个比较明显的做法,就是离线处理。
我们把边按权值排序,然后不断加入边,处理询问,加边的时候可以启发式合并。
然后时间复杂度就是 O(nlog2n) 了。
然后就有了另一个题:bzoj3551 peaks加强版(强制在线)
这个我也是才学,不会讲,还是放po姐的博客吧:
http://blog.csdn.net/PoPoQQQ/article/details/41348785
那个图就是样例输入的重构树,大家可以自己模拟一下它是如何建出来的。
还有,那几个性质好好想一想。
然后应该就懂了,po姐还是讲得很好的。
过程:
1.读入
2.离散化
3.做kruskal,同时构造出重构树
4.在kruskal重构树上dfs一遍,然后找出dfs序(只有叶子)和每个节点的子树中的叶子数量,以及每个点的子树在dfs序上的起始位置
5.在dfs序上建主席树
6.利用kruskal重构树的性质,我们只需要在dfs时处理出来每个点向上跳2^j步的位置,然后倍增地去跳到权值<=限制权值的深度最小的点,它的子树就是限制下可以到达的所有节点。
7.对于查询,我们在主席树上利用之前的信息直接查询即可。
跑得好慢
分别是9632ms(3545),17124ms(3551)
都是卡线呀(滑稽)
代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<vector>
#include<queue>
#define ll long long
using namespace std;
inline int read(){
    int x=0,f=1;char ch=' ';
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')f=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-48,ch=getchar();
    return x*f;
}
const int N=1e5+500;
const int M=5e5+500;
struct kedge{
    int x,y,l;
    inline bool operator < (const kedge& b) const {return l<b.l;}
}e[M];
struct edge{
    int to,next;
}e1[M<<1],e2[M+N];
int n,m,q,tot1,tot2,tot,hash_cnt;
int h[N],fa[N+M],head1[N],head2[N+M],w[N+M],dep[N+M],b[N];
inline void addedge1(int x,int y){e1[++tot1].to=y;e1[tot1].next=head1[x];head1[x]=tot1;}
inline void addedge2(int x,int y){e2[++tot2].to=y;e2[tot2].next=head2[x];head2[x]=tot2;}
inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline void kruskal(){
    for(int i=1;i<=n;i++)fa[i]=i,w[i]=h[i];
    sort(e+1,e+m+1);tot=n;int j=n;
    for(int i=1;i<=m;i++){
        int x=e[i].x;int y=e[i].y;int l=e[i].l;
        int u=find(x);int v=find(y);
        if(u==v)continue;
        w[++tot]=l;
        addedge2(tot,u);addedge2(tot,v);
        fa[u]=fa[v]=fa[tot]=tot;
        j--;if(j==1)break;
    }
}
struct node{
    int ls,rs,size;
}t[N*25];
int st[20][N+M],size[N+M],dfn[N+M],dfn_clock,cnt,root[N+M],mp[N+M],start[N+M];
inline void dfs(int x){
    for(int k=1;k<=19&&(1<<k)<=dep[x];k++){
        st[k][x]=st[k-1][st[k-1][x]];
    }
    size[x]=0;start[x]=dfn_clock+1;
    if(x<=n)dfn[++dfn_clock]=x,size[x]++,mp[x]=dfn_clock;
    for(int i=head2[x];i;i=e2[i].next){
        int u=e2[i].to;
        st[0][u]=x;dep[u]=dep[x]+1;
        dfs(u);
        size[x]+=size[u];
    }
}
inline void build(int &now,int l,int r){
    now=++cnt;
    if(l==r)return;
    int mid=(l+r)>>1;
    build(t[now].ls,l,mid);
    build(t[now].rs,mid+1,r);
}
inline void insert(int &now,int pre,int l,int r,int x){
    now=++cnt;
    t[now]=t[pre];
    t[now].size++;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(x<=mid)insert(t[now].ls,t[pre].ls,l,mid,x);
    else insert(t[now].rs,t[pre].rs,mid+1,r,x);
}
inline int kth(int now,int pre,int l,int r,int k){
    if(l==r){
        if(k>t[now].size-t[pre].size)return -1;
        else return b[l];
    }
    int rsize=t[t[now].rs].size-t[t[pre].rs].size;
    int mid=(l+r)>>1;
    if(k<=rsize)return kth(t[now].rs,t[pre].rs,mid+1,r,k);
    else return kth(t[now].ls,t[pre].ls,l,mid,k-rsize);
}
inline void make(){
    build(root[0],1,hash_cnt);
    for(int i=1;i<=dfn_clock;i++){
        h[dfn[i]]=lower_bound(b+1,b+hash_cnt+1,h[dfn[i]])-b;
        insert(root[i],root[i-1],1,hash_cnt,h[dfn[i]]);
    }
}
inline int jump(int x,int V){
    for(int k=19;k>=0;k--){
        if(st[k][x]&&w[st[k][x]]<=V){
            x=st[k][x];
        }
    }
    return x;
}
int main(){
    n=read();m=read();q=read();
    for(int i=1;i<=n;i++)h[i]=read(),b[i]=h[i];
    sort(b+1,b+n+1);
    hash_cnt=unique(b+1,b+n+1)-b-1;
    for(int i=1;i<=m;i++){
        int x=read(),y=read(),l=read();
        addedge1(x,y);addedge1(y,x);
        e[i].x=x;e[i].y=y;e[i].l=l;
    }
    kruskal();
    dep[tot]=1;
    dfs(tot);
    make();
    int lastans=0;
    while(q--){
        int x=read()^lastans,V=read()^lastans,k=read()^lastans;
        int top=jump(x,V);
        lastans=kth(root[start[top]+size[top]-1],root[start[top]-1],1,hash_cnt,k);
        printf("%d\n",lastans);
        if(lastans==-1)lastans=0;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值