【算法简介】
算法流程和Kruskal生成树类似,把边排序后,依据并查集加边即可,两个点认一个新的节点父亲,这个点的点权就是当前边权
所以我们最后得到了一个有2n-1个节点的带点权的树
sort(e+1,e+m+1,cmp);
for(int i=1;i<=n*2;i++) fa[i]=i;
cnt=n;
for(int i=1;i<=m;i++)
{
int fx=find(e[i].u),fy=find(e[i].v);
if(fx!=fy)
{
fa[fx]=fa[fy]=++cnt;
ew[cnt]=e[i].val;
add(cnt,fx); add(cnt,fy);
in[fx]++; in[fy]++;
}
}
性质:
1、u,v经过的最大值,就是重构树上lca的值
如果从小到大加边,可以考虑越加边图的连通性越强,也就是说u->v的最大值,就是刚好加了某个点,使得u,v联通,这个点就是刚刚加入的u,v的lca
2、重构树中代表原树中的点的节点全是叶子节点,其余节点都代表了一条边的边权。
【例题】P4197 Peaks
【题意】
有点权也有边权的图中一点v开始走不超过x的边权,能够走到的所有节点的点权中第k大的值
【分析】
建出Kruskal重构树,问题就转化为了求子树中的第k大问题,就能想到主席树了
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
const int maxm=5e5+5;
const int inf=0x3f3f3f3f;
int n,m,q,root[maxn];
int h[maxn],in[maxn],fa[maxn];
struct edge
{
int u,v,val;
}e[maxm];
bool cmp(edge a,edge b)
{
return a.val<b.val;
}
int cnt,ew[maxn];
int find(int x)
{
if(x==fa[x]) return x;
return fa[x]=find(fa[x]);
}
int head[maxn],tot;
struct edgee
{
int to,nxt;
}G[maxn<<1];
void add(int x,int y)
{
G[++tot].to=y; G[tot].nxt=head[x]; head[x]=tot;
}
int intime[maxn],f[maxn][20],siz[maxn],outime[maxn],pos[maxn],dfstime;
void dfs(int x)
{
intime[x]=++dfstime; pos[dfstime]=x;
//siz[x]=1;
for(int i=head[x];i;i=G[i].nxt)
{
int to=G[i].to;
if(to==fa[x]) continue;
f[to][0]=x;
dfs(to);
siz[x]+=siz[to];
}
if(!siz[x]) siz[x]=1;
outime[x]=dfstime;
}
void lca_init()
{
for(int k=1;k<=19;k++)
for(int i=1;i<=n*2;i++)
f[i][k]=f[f[i][k-1]][k-1];
}
struct segtree
{
int l,r,sum;
}tr[maxn<<5];
int etot;
void update(int &now,int L,int R,int pos,int val)
{
tr[++etot]=tr[now]; tr[etot].sum+=val;
now=etot;
if(L==R) return;
int mid=L+R>>1;
if(pos<=mid) update(tr[now].l,L,mid,pos,val);
if(mid<pos) update(tr[now].r,mid+1,R,pos,val);
}
int calc(int rx,int ry,int L,int R,int k)
{
if(L==R) return L;
int sum=tr[tr[ry].r].sum-tr[tr[rx].r].sum;
int mid=L+R>>1;
if(sum>=k) return calc(tr[rx].r,tr[ry].r,mid+1,R,k);
else return calc(tr[rx].l,tr[ry].l,L,mid,k-sum);
}
int query(int v,int x,int k)
{
for(int i=18;i>=0;i--) if(f[v][i] && ew[f[v][i]]<=x) v=f[v][i];
if(siz[v]<k) return -1;
int l=intime[v],r=outime[v];
return calc(root[l-1],root[r],0,inf,k);
}
int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++)
scanf("%d",&h[i]);
int v,x,k;
for(int i=1;i<=m;i++)
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].val);
sort(e+1,e+m+1,cmp);
for(int i=1;i<=n*2;i++) fa[i]=i;
cnt=n;
for(int i=1;i<=m;i++)
{
int fx=find(e[i].u),fy=find(e[i].v);
if(fx!=fy)
{
fa[fx]=fa[fy]=++cnt;
ew[cnt]=e[i].val;
add(cnt,fx); add(cnt,fy);
in[fx]++; in[fy]++;
}
}
for(int i=1;i<=cnt;i++) if(!in[i]) dfs(i);
lca_init();
for(int i=1;i<=cnt;i++)
{
root[i]=root[i-1];
if(pos[i]<=n) update(root[i],0,inf,h[pos[i]],1);
}
for(int i=1;i<=q;i++)
{
scanf("%d%d%d",&v,&x,&k);
printf("%d\n",query(v,x,k));
}
return 0;
}