背景:
下午翘课,因为 要拍视频,懒得去。
题目传送门:
https://www.luogu.org/problem/P4197
题意:
n
n
n个点,
m
m
m条边,有点权,有边权。
多组询问,求只经过权值小于等于
x
x
x的边的第
k
k
k大的点的权值。
思路:
容易想到离线按照询问点权升序,本质就是一棵树(由于已经连通,多余的边是废的),显然有一个主席树+主席树合并的做法,但是考虑到主席树合并我从来没有打过,而且树上的主席树实现应该比较繁琐,我还是放弃了。
发现正解是克鲁斯卡尔重构树。
于是学习了一发。
这是什么东西呢?
先讲一下它的构造过程吧:
类似克鲁斯卡尔最小生成树的方法,按照边权升序,若当前的点对
(
x
,
y
)
(x,y)
(x,y)并不连通,则新建一个点
T
T
T,让
T
T
T作为这两个点的祖先,且此时
T
T
T的边权为边
(
x
,
y
)
(x,y)
(x,y)的权值。
这样建出的树有什么性质吗?
[
1
]
.
[1].
[1].这一定是一个大根堆。
显然每一次都是两个点的合并,因此是二叉树;而越往后的边边权越大,因此越往后新建的点点权越大,而新的点必然统治旧的点,因此是一个大根堆。
[
2
]
.
[2].
[2].任意两条路径上的点权的最大值为它们
lca
\text{lca}
lca的值。
这也比较显然,因为这两个点第一次连通必然通过
lca
\text{lca}
lca,而这是一个大根堆,因此得证。
那么你用一个并查集维护,然后实现建树的过程即可。
剩下的就是倍增找到某一个点的最远的祖先,使得他的权值小于等于
x
x
x,然后在这个点的子树内查找第
k
k
k大的值即可,可用
dfs
\text{dfs}
dfs序+主席树实现。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct node1{int x,y,next;} a[500010];
struct node2{int x,y,z;} b[500010];
int c[200010],last[200010],fa[200010],val[200010],id[200010],f[200010][20],size[200010];
int ls[5000010],rs[5000010],root[5000010],tot[5000010];
int n,m,q,len=0,ma=0;
bool cmp(node2 x,node2 y)
{
return x.z<y.z;
}
int find(int x)
{
return fa[x]==x?x:fa[x]=find(fa[x]);
}
void ins(int x,int y)
{
a[++len]=(node1){x,y,last[x]}; last[x]=len;
}
void change(int last,int &now,int x,int l,int r)
{
if(!now) now=++len;
tot[now]=tot[last]+1;
if(l==r) return;
int mid=(l+r)/2;
if(x<=mid)
{
rs[now]=rs[last];
change(ls[last],ls[now],x,l,mid);
}
else
{
ls[now]=ls[last];
change(rs[last],rs[now],x,mid+1,r);
}
}
int solve(int x,int y,int k,int l,int r)
{
if(tot[y]<k) return -1;
if(l==r) return l;
int tmp=tot[rs[y]]-tot[rs[x]],mid=(l+r)/2;
return k<=tmp?solve(rs[x],rs[y],k,mid+1,r):solve(ls[x],ls[y],k-tmp,l,mid);
}
int u=0;
void dfs(int x)
{
id[x]=++u;
size[x]=1;
if(x<=n) change(root[id[x]-1],root[id[x]],c[x],0,ma); else root[id[x]]=root[id[x]-1];
for(int i=1;i<=19;i++)
f[x][i]=f[f[x][i-1]][i-1];
for(int i=last[x];i;i=a[i].next)
{
int y=a[i].y;
f[y][0]=x;
dfs(y);
size[x]+=size[y];
}
}
int find_(int x,int y)
{
for(int i=19;i>=0;i--)
if(f[x][i]&&val[f[x][i]]<=y) x=f[x][i];
return x;
}
int main()
{
int x,y,k;
scanf("%d %d %d",&n,&m,&q);
for(int i=1;i<=n;i++)
{
scanf("%d",&c[i]);
ma=max(ma,c[i]);
fa[i]=i;
}
for(int i=1;i<=m;i++)
scanf("%d %d %d",&b[i].x,&b[i].y,&b[i].z);
sort(b+1,b+m+1,cmp);
int cnt=n;
for(int i=1;i<=m;i++)
{
int t1=find(b[i].x),t2=find(b[i].y);
if(t1==t2) continue;
fa[++cnt]=cnt;
fa[t1]=fa[t2]=cnt;
val[cnt]=b[i].z;
ins(cnt,t1),ins(cnt,t2);
if(cnt==n+n-1) break;
}
len=0;
dfs(cnt);
for(int i=1;i<=q;i++)
{
scanf("%d %d %d",&x,&y,&k);
int t=find_(x,y);
printf("%d\n",solve(root[id[t]-1],root[id[t]+size[t]-1],k,0,ma));
}
}