题目链接:点击查看
题目大意:有 n 座山峰,每座山峰有他的高度 h[ i ] ,有些山峰之间有双向道路相连,共 m 条路径,每条路径有一个困难值,这个值越大表示越难走。
现在有 q 组询问,每组询问询问从点 v 开始只经过困难值小于等于 x 的路径所能到达的山峰中第 k 高的山峰,如果无解输出 −1。
题目分析:因为有困难值的限制,所以可以对整个图跑克鲁斯卡尔重构树,如果对点 v 来说,只能走小于等于 x 的路径,可以树上倍增找到权值小于等于 x 的,深度最浅的祖先,显然这个祖先子树中的所有点都是可以从点 v 到达的,接下来找第 k 小,就是主席树的工作了,dfs 序上建一下主席树就好了
注意,题目中需要求的是第 k 大,而不是第 k 小
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=1e6+100;
int h[N],f[N],val[N],dp[N][25],L[N],R[N],n,nn,m,q,tot,limit;
bool vis[N];
/*主席树*/
struct Node
{
int l,r;
int sum;
}tree[N*20];
int cnt,root[N];
void init()
{
root[0]=0;
tree[0].l=tree[0].r=tree[0].sum=0;
cnt=1;
}
void update(int num,int &k,int l,int r)
{
tree[cnt++]=tree[k];
k=cnt-1;
tree[k].sum++;
if(l==r)
return;
int mid=l+r>>1;
if(num<=mid)
update(num,tree[k].l,l,mid);
else
update(num,tree[k].r,mid+1,r);
}
int query(int i,int j,int k,int l,int r)//i:左端点根节点编号,j:右端点根节点编号,k:第k大的数,l,r:区间[l,r]
{
int d=tree[tree[j].r].sum-tree[tree[i].r].sum;
if(l==r)
return l;
int mid=l+r>>1;
if(k<=d)
return query(tree[i].r,tree[j].r,k,mid+1,r);
else
return query(tree[i].l,tree[j].l,k-d,l,mid);
}
/*主席树*/
struct Edge
{
int u,v,w;
bool operator<(const Edge& t)const
{
return w<t.w;
}
}edge[N];
vector<int>node[N];//邻接表
/*dfs序+树上倍增*/
void dfs(int u,int fa)
{
vis[u]=true;
dp[u][0]=fa;
for(int i=1;i<=limit;i++)
dp[u][i]=dp[dp[u][i-1]][i-1];
L[u]=++tot;
root[tot]=root[tot-1];
if(u<=n)
update(h[u],root[tot],1,nn);
for(auto v:node[u])
dfs(v,u);
R[u]=tot;
}
/*dfs序+树上倍增*/
/*并查集+克鲁斯卡尔重构树*/
int find(int x)
{
return f[x]==x?x:f[x]=find(f[x]);
}
void Ex_Kruskal()
{
int index=n;
for(int i=1;i<=n<<1;i++)
f[i]=i;
sort(edge+1,edge+1+m);
for(int i=1;i<=m;i++)
{
int xx=find(edge[i].u),yy=find(edge[i].v);
if(xx!=yy)
{
f[xx]=f[yy]=++index;
val[index]=edge[i].w;
node[index].push_back(xx);
node[index].push_back(yy);
}
}
}
/*并查集+克鲁斯卡尔重构树*/
/*离散化*/
vector<int>disc;
void discreate()
{
sort(disc.begin(),disc.end());
disc.erase(unique(disc.begin(),disc.end()),disc.end());
nn=disc.size();
for(int i=1;i<=n;i++)
h[i]=lower_bound(disc.begin(),disc.end(),h[i])-disc.begin()+1;
}
/*离散化*/
int get_pos(int u,int up)//树上倍增找满足的祖先
{
for(int i=20;i>=0;i--)
if(dp[u][i]!=0&&val[dp[u][i]]<=up)
u=dp[u][i];
return u;
}
int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
init();
scanf("%d%d%d",&n,&m,&q);
limit=log2(n)+1;
for(int i=1;i<=n;i++)
{
scanf("%d",h+i);
disc.push_back(h[i]);
}
discreate();
for(int i=1;i<=m;i++)
scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
Ex_Kruskal();
for(int i=1;i<=n;i++)
if(!vis[i])
dfs(find(i),0);
while(q--)
{
int v,x,k;
scanf("%d%d%d",&v,&x,&k);
v=get_pos(v,x);
if(tree[root[R[v]]].sum-tree[root[L[v]-1]].sum<k)
puts("-1");
else
printf("%d\n",disc[query(root[L[v]-1],root[R[v]],k,1,nn)-1]);
}
return 0;
}