BZOJ3545 Peaks 离线处理+线段树合并

题意:

  在Bytemountains有N座山峰,每座山峰有他的高度h_i。有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走,现在有Q组询问,每组询问询问从点v开始只经过困难值小于等于x的路径所能到达的山峰中第k高的山峰,如果无解输出-1。

分析:

  我们把题目中的限制分离出来:

  1. 困难值不超过x。

  2. 能达到的第k高的山峰。

  如果没有限制1,我们对每个连通块建线段树即可,如果没有限制2,我们我们可以选择按照kruskal的思想,按照困难值从小到大加便,离线处理询问。

  现在两个限制都在,所以我们考虑使两种算法兼容,所以我们连边时如果两个端点分别属于不同的连通块,我们就合并两个连通块,需要处理的问题是并查集合并和线段树合并。同时离线处理询问即可。

  这题还有一个加强版,是强制在线的版本,应该是一道kruskal重构树的练手题。

代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N=100005;
 4 struct que{int a,b,c,id,ok;}a[N*10];
 5 struct seg{int ls,rs,v;}t[N*50];
 6 int n,m,q,tot=0,nm1[N],nm2[N],fa[N],rt[N],ans[N*5];
 7 bool cmp(que a,que b){
 8     return a.c==b.c?a.ok<b.ok:a.c<b.c;
 9 } int get(int x){
10     return fa[x]==x?x:fa[x]=get(fa[x]);
11 } void update(int &x,int l,int r,int v){
12     if(!x) x=++tot;t[x].v=1;
13     if(l==r) return ;int mid=l+r>>1;
14     if(v<=mid) update(t[x].ls,l,mid,v);
15     else update(t[x].rs,mid+1,r,v);
16 } int query(int x,int l,int r,int p){
17     if(l==r) return l;int mid=l+r>>1;
18     if(p<=t[t[x].ls].v) 
19     return query(t[x].ls,l,mid,p);
20     else return 
21     query(t[x].rs,mid+1,r,p-t[t[x].ls].v);
22 } int merge(int x,int y){
23     if(!x||!y) return x|y;
24     if(!t[x].ls&&!t[x].rs){
25         t[x].v+=t[y].v;return x;
26     } t[x].ls=merge(t[x].ls,t[y].ls);
27     t[x].rs=merge(t[x].rs,t[y].rs);
28     t[x].v=t[t[x].ls].v+t[t[x].rs].v;
29     return x;
30 } int main(){
31     scanf("%d%d%d",&n,&m,&q);
32     for(int i=1;i<=n;i++)
33     scanf("%d",&nm1[i]),
34     nm2[i]=nm1[i],fa[i]=i;
35     sort(nm2+1,nm2+n+1);
36     for(int i=1;i<=n;i++)
37     nm1[i]=lower_bound(nm2+1,nm2+1+n,nm1[i])-nm2;
38     for(int i=1,x,y,z;i<=m;i++)
39     scanf("%d%d%d",&x,&y,&z),
40     a[i]=(que){x,y,z,0,0};
41     for(int i=m+1,x,y,z;i<=m+q;i++)
42     scanf("%d%d%d",&x,&y,&z),
43     a[i]=(que){x,z,y,i-m,1};
44     sort(a+1,a+1+m+q,cmp);
45     for(int i=1;i<=n;i++)
46     update(rt[i],1,n,nm1[i]);
47     for(int i=1;i<=m+q;i++)
48     if(!a[i].ok){
49         int x=get(a[i].a),y=get(a[i].b);
50         if(x!=y) fa[x]=y,
51         rt[y]=merge(rt[x],rt[y]);
52     } else{
53         int x=get(a[i].a);
54         if(t[rt[x]].v<a[i].b)
55         ans[a[i].id]=-1;
56         else ans[a[i].id]=
57         nm2[query(rt[x],1,n,t[rt[x]].v-a[i].b+1)];
58     } for(int i=1;i<=q;i++) 
59     printf("%d\n",ans[i]);return 0;
60 }
线段树合并

猜你喜欢

转载自www.cnblogs.com/Alan-Luo/p/10391789.html