bzoj 3545: [ONTAK2010]Peaks
Submit: 1124 Solved: 304
[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
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
1
-1
8
HINT
【数据范围】
N<=10^5, M,Q<=5*10^5,h_i,c,x<=10^9。
题解:
把M+Q个操作全部存起来,准备离线操作,添加路径的flag为1,询问的flag为0,id为i-M方便记录答案。以困难值为第一关键字,flag为第二关键字排序。这样碰到询问操作则当前的的所有连通块之间的边的困难值满足要求,记录连通块之间的信息用并查集维护一下就好了。
本题用可持久化线段树的来维护,建立N棵线段树,如果a和b之间连了一条边,则把线段树a和线段树b合并起来,由于所有线段树的形态均相同,所以只要对应的权值相加减即可。
具体看代码。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<cmath> 6 #include<algorithm> 7 #include<queue> 8 #include<vector> 9 using namespace std; 10 const int maxn=1e5+10; 11 inline int read(){ 12 int x=0,f=1;char ch=getchar(); 13 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 14 while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} 15 return x*f; 16 } 17 int N,M,Q,ans[5*maxn]; 18 int H[maxn],h[maxn],hash[maxn],tot; 19 int root[maxn],siz; 20 struct Tree{ 21 int lc,rc,w; 22 }tr[maxn*50]; 23 inline int find(int x){ 24 int l=1,r=tot; 25 while(l<=r){ 26 int mid=(l+r)>>1; 27 if(hash[mid]<x) l=mid+1; 28 else if(hash[mid]>x) r=mid-1; 29 else if(hash[mid]==x) return mid; 30 } 31 return l; 32 } 33 34 struct node{ 35 int a,b,w; 36 int flag,id; 37 }g[maxn*12]; 38 int cmp(const node&q,const node&e){ 39 return q.w<e.w||(q.w==e.w&&q.flag>e.flag); 40 } 41 42 int fa[maxn]; 43 inline int getfa(int r){ 44 if(r!=fa[r]) fa[r]=getfa(fa[r]); 45 return fa[r]; 46 } 47 48 inline void insert(int &i,int l,int r,int w){ 49 i=++siz; 50 tr[i].w=1; 51 if(l==r) return ; 52 int mid=(l+r)>>1; 53 if(w<=mid) insert(tr[i].lc,l,mid,w); 54 else insert(tr[i].rc,mid+1,r,w); 55 } 56 inline int query(int i,int l,int r,int k){ 57 if(l==r) return l; 58 int mid=(l+r)>>1; 59 if(tr[tr[i].lc].w>=k)return query(tr[i].lc,l,mid,k); 60 else return query(tr[i].rc,mid+1,r,k-tr[tr[i].lc].w); 61 } 62 inline int merge(int x,int y){ 63 if(x==0) return y; 64 if(y==0) return x; 65 if(tr[x].lc==0&&tr[x].rc==0){ 66 tr[x].w=tr[x].w+tr[y].w; 67 return x; 68 } 69 tr[x].lc=merge(tr[x].lc,tr[y].lc); 70 tr[x].rc=merge(tr[x].rc,tr[y].rc); 71 tr[x].w=tr[tr[x].lc].w+tr[tr[x].rc].w; 72 return x; 73 } 74 int main(){ 75 N=read(); M=read(); Q=read(); 76 for(int i=1;i<=N;i++) H[i]=read(),h[i]=H[i]; 77 sort(h+1,h+N+1); 78 hash[++tot]=h[1]; 79 for(int i=2;i<=N;i++){//按照山的高度离散化+去重 80 if(h[i]!=h[i-1]) hash[++tot]=h[i]; 81 } 82 83 for(int i=1,a,b,w;i<=M+Q;i++){ 84 a=read(); b=read(); w=read(); 85 if(i<=M){ 86 g[i].flag=1; 87 g[i].a=a; g[i].b=b; g[i].w=w; 88 } 89 else{ 90 g[i].id=i-M; 91 g[i].a=a; g[i].w=b; g[i].b=w; 92 } 93 } 94 sort(g+1,g+M+Q+1,cmp); 95 for(int i=1;i<=N;i++) fa[i]=i; 96 for(int i=1;i<=N;i++){ 97 int w=find(H[i]); 98 insert(root[i],1,tot,w); 99 } 100 for(int i=1;i<=M+Q;i++){ 101 if(g[i].flag==1){//在图中加入一条边 102 int p=getfa(g[i].a),q=getfa(g[i].b); 103 if(p!=q){ 104 fa[p]=q; 105 root[q]=merge(root[p],root[q]); 106 } 107 } 108 else{//询问 109 int v=getfa(g[i].a); 110 if(tr[root[v]].w<g[i].b) ans[g[i].id]=-1; 111 else ans[g[i].id]=hash[query(root[v],1,tot,tr[root[v]].w-g[i].b+1)]; 112 } 113 } 114 for(int i=1;i<=Q;i++){ 115 printf("%d\n",ans[i]); 116 } 117 return 0; 118 }