3545: [ONTAK2010]Peaks
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 2621 Solved: 722
[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。
Source
By Sbullet
sol:
我们愉快的来做在线版本吧
因为要求权值小于等于某个值,那么思路显然有两种,一种是二分权值,但是发现这样想不太对啊。还有一种思路就是考虑顺序加边。
这题我们考虑从大到小加边,那么两个点显然会把两个联通块连接起来,并且当前加的边肯定是大联通块中最小的边。
因为要求k大数,所以我们在每个 联通块中都维护一个权值线段树。然后接下来又有两种做法,显然是要用并查集和权值线段树的,为了在线回答,所以我们把这两个东西都可持久化了就能做了(滑稽)。还有一种就是kr重构树了。
我们把两个点x,y并起来的时候,新建一个点z使得z的权值为xy边权,且z为xy的父亲,然后线段树合并。
新图有一些很好的性质/属性
1、二叉树
2、叶节点才是原图的点
3、对于一个点x,x子树中的某个叶节点到达x子树中其他叶节点经过的边权就是经过的点权中的最值
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
using namespace std;
int n,m,len,q;
inline int read()
{
char c;
int res,flag=0;
while((c=getchar())>'9'||c<'0') if(c=='-')flag=1;
res=c-'0';
while((c=getchar())>='0'&&c<='9') res=(res<<3)+(res<<1)+c-'0';
return flag?-res:res;
}
const int N=210010;
const int M=4000000;
int tot,fa[19][N],father[N],val[N],p;
int sum[M],son[M][2];
inline int merge(int x,int y,int l,int r)
{
if(!x) return y;
if(!y) return x;
int now=++tot;
sum[now]=sum[x]+sum[y];
if(l==r) return now;
int mid=l+r>>1;
son[now][0]=merge(son[x][0],son[y][0],l,mid);
son[now][1]=merge(son[x][1],son[y][1],mid+1,r);
return now;
}
int Ma[19][N];
inline int lca(int x,int Max)
{
for(int j=18;j>=0;--j)
if(fa[j][x]&&Ma[j][x]<=Max)
x=fa[j][x];
return x;
}
int b[N],h[N];
inline int query(int k,int l,int r,int num)
{
if(sum[k]<num) return -1;
if(l==r) return b[l];
int mid=l+r>>1;
if(sum[son[k][1]]>=num) return query(son[k][1],mid+1,r,num);
else return query(son[k][0],l,mid,num-sum[son[k][1]]);
}
struct cc
{
int x,y,z;
inline bool friend operator <(const cc &a,const cc &b)
{
return a.z<b.z;
}
}a[510000];
inline int find(int x)
{
return father[x]==x?x:father[x]=find(father[x]);
}
inline void insert(int &k,int l,int r,int x)
{
k=++tot;
sum[k]++;
if(l==r) return;
int mid=l+r>>1;
if(mid>=x) insert(son[k][0],l,mid,x);
else insert(son[k][1],mid+1,r,x);
}
int rt[N];
int p_n;
int main()
{
// freopen("3545.in","r",stdin);
// freopen("3545.out","w",stdout);
n=read();
m=read();
q=read();
p_n=n;
for(int i=1;i<=n;++i) h[i]=b[i]=read();
sort(b+1,b+1+n);
len=unique(b+1,b+1+n)-b-1;
for(int i=1;i<=n;++i) h[i]=lower_bound(b+1,b+1+len,h[i])-b;
int x,y,z;
for(int i=1;i<=m;++i)
{
a[i].x=read();
a[i].y=read();
a[i].z=read();
}
for(int i=1;i<=n;++i)
insert(rt[i],1,len,h[i]);
for(int i=1;i<=n;++i) father[i]=i;
sort(a+1,a+1+m);
int cnt=0;
for(int i=1;i<=m;++i)
{
int fx=find(a[i].x),fy=find(a[i].y);
if(fx!=fy)
{
fa[0][fx]=fa[0][fy]=father[fx]=father[fy]=++p_n;
father[p_n]=p_n;
val[p_n]=a[i].z;
Ma[0][fx]=val[p_n];
Ma[0][fy]=val[p_n];
rt[p_n]=merge(rt[fx],rt[fy],1,len);
++cnt;
if(cnt==n-1)
break;
}
}
for(int j=1;j<=18;++j)
for(int i=1;i<=p_n;++i)
{
fa[j][i]=fa[j-1][fa[j-1][i]];
Ma[j][i]=max(Ma[j-1][i],Ma[j-1][fa[j-1][i]]);
}
int v,k;
int last=0;
while(q--)
{
v=read();
x=read();
k=read();
y=lca(v,x);
printf("%d\n",last=query(rt[y],1,len,k));
if(last==-1) last=0;
}
}