克鲁斯卡尔重构树
用途
用来维护图上两点, u u u到 v v v的路径中,最大边权的最小值(或最小边权的最大值)
实现
像克鲁斯卡尔一样,先把边权排序,然后用并查集 建出一颗重构树,点权就是当前边的边权,然后再用树链剖分dfs两次,方便之后求
l
c
a
lca
lca;
如果原图不是连通图,那么建出的就是重构森林,以每个连通块最后加入的点为根;如果原图是连通图,那么最后加入的节点,就是重构树的根。
代码
void work(){
cnt=n;
for(int i=1;i<=n;i++)ff[i]=i;
sort(z+1,z+1+m,cmp);
for(int i=1;i<=m;i++){
int a=find(z[i].x);
int b=find(z[i].y);
if(a==b)continue;
val[++cnt]=z[i].w;
ff[cnt]=ff[a]=ff[b]=cnt;
v[cnt].push_back(a);
v[cnt].push_back(b);
v[a].push_back(cnt);
v[b].push_back(cnt);
}
for(int i=1;i<=cnt;i++){
if(vis[i])continue;
int a=find(i);
dfs1(a,0);dfs2(a,0);
}
/*
//如果图是联通的,那么只需要从最后一个点开始处理即可
dfs1(cnt,0);dfs2(cnt,0);
*/
}
例题
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n,m,q;
struct node{
int x,y,w;
}z[30040];
bool cmp(node a,node b){
return a.w<b.w;
}
int ff[30050],fa[30050],siz[30050],son[30050],dep[30050],top[30050];
int val[30050],vis[30050],cnt;
vector<int>v[30050];
int find(int x){
if(x==ff[x])return x;
return ff[x]=find(ff[x]);
}
void dfs1(int x,int pre){
fa[x]=pre;dep[x]=dep[pre]+1;
siz[x]=1;vis[x]=1;
for(int i=0;i<v[x].size();i++){
int to=v[x][i];
if(to==pre)continue;
dfs1(to,x);
siz[x]+=siz[to];
if(siz[to]>siz[son[x]])son[x]=to;
}
}
void dfs2(int x,int pre){
if(son[pre]==x)top[x]=top[pre];
else top[x]=x;
if(son[x])dfs2(son[x],x);
for(int i=0;i<v[x].size();i++){
int to=v[x][i];
if(to==pre||to==son[x])continue;
dfs2(to,x);
}
}
void work(){
cnt=n;
for(int i=1;i<=n;i++)ff[i]=i;
sort(z+1,z+1+m,cmp);
for(int i=1;i<=m;i++){
int a=find(z[i].x);
int b=find(z[i].y);
if(a==b)continue;
val[++cnt]=z[i].w;
ff[cnt]=ff[a]=ff[b]=cnt;
v[cnt].push_back(a);
v[cnt].push_back(b);
v[a].push_back(cnt);
v[b].push_back(cnt);
}
for(int i=1;i<=cnt;i++){
if(vis[i])continue;
int a=find(i);
dfs1(a,0);dfs2(a,0);
}
}
int lca(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]>dep[top[y]])swap(x,y);
y=fa[top[y]];
}
if(dep[x]>dep[y])swap(x,y);
return x;
}
int main(){
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&z[i].x,&z[i].y,&z[i].w);
}
work();
for(int i=1;i<=q;i++){
int a,b;scanf("%d%d",&a,&b);
printf("%d\n",val[lca(a,b)]);
}
return 0;
}
题意:
给你一张 n n n个点, m m m条边的图,在线询问 q q q组,每次询问从 v v v作为出发点,只能经过边权小于等于 x x x的边,求点权第 k k k大的点。
思路:
首先当然是,克鲁斯卡尔重构一下,这样只要从
v
v
v开始,找一个最浅的祖先
y
y
y,满足权值小于等于
x
x
x;
那么
y
y
y的子树包含的叶子点,就是满足条件的集合。
那么问题就转变成了,求这个集合第
k
k
k大;
那么维护子树信息,能用什么数据结构呢:
主席树+dfs序,那么子树就是线性的一段,直接查就行;
(作死想到了线段树合并
可惜无了,线段树合并貌似只能维护离线询问,在所有都合并了之后,权值线段树的结构就变了,如果万能的友友 有能够处理的方法,评论区告诉我~
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n,m,q;
struct node{
int x,y,w;
}z[500040];
int h[100050];
int hh[100050];
int len=0;
int val[200050],ff[200050],fa[200050][20],dep[200050],siz[200050];
int cnt;
vector<int>v[200050];
int tot=0;
int root[200050];
int ls[200050],rs[200050];
struct nod{
int l,r,num;
}zz[4000050];
bool cmp(node a,node b){
return a.w<b.w;
}
int clone(int x){
tot++;
zz[tot]=zz[x];
return tot;
}
void update(int id1,int &id2,int l,int r,int x){
id2=clone(id1);zz[id2].num++;
if(l>=r)return ;
int mid=l+r>>1;
if(x<=mid)update(zz[id1].l,zz[id2].l,l,mid,x);
else update(zz[id1].r,zz[id2].r,mid+1,r,x);
}
int query(int id1,int id2,int l,int r,int k){
if(l>=r)return l;
int mid=l+r>>1;
int num=zz[zz[id2].r].num-zz[zz[id1].r].num;
if(k<=num)return query(zz[id1].r,zz[id2].r,mid+1,r,k);
else return query(zz[id1].l,zz[id2].l,l,mid,k-num);
}
int num=0;
int find(int x){
if(x==ff[x])return x;
return ff[x]=find(ff[x]);
}
void dfs(int x,int pre){
fa[x][0]=pre;dep[x]=dep[pre]+1;
siz[x]=1;
for(int i=1;i<20;i++)fa[x][i]=fa[fa[x][i-1]][i-1];
int f=0;
ls[x]=1e9,rs[x]=0;
for(int i=0;i<v[x].size();i++){
int to=v[x][i];
if(to==pre)continue;
dfs(to,x);
siz[x]+=siz[to];
ls[x]=min(ls[x],ls[to]);
rs[x]=max(rs[x],rs[to]);
f=1;
}
if(!f){
int pos=lower_bound(hh+1,hh+1+len,h[x])-hh;
num++;
update(root[num-1],root[num],1,len,pos);
ls[x]=rs[x]=num;
}
}
void work(){
cnt=n;
for(int i=1;i<=n;i++)ff[i]=i;
sort(z+1,z+1+m,cmp);
for(int i=1;i<=m;i++){
int a=find(z[i].x);
int b=find(z[i].y);
if(a==b)continue;
val[++cnt]=z[i].w;
ff[cnt]=ff[a]=ff[b]=cnt;
v[cnt].push_back(a);
v[cnt].push_back(b);
v[a].push_back(cnt);
v[b].push_back(cnt);
}
dfs(cnt,0);
}
int find(int x,int y){
val[0]=1e9+7;
for(int i=19;i>=0;i--){
if(val[fa[x][i]]<=y)x=fa[x][i];
}
return x;
}
int main(){
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++){
scanf("%d",&h[i]);
hh[i]=h[i];
}
sort(hh+1,hh+1+n);
len=unique(hh+1,hh+1+n)-hh-1;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&z[i].x,&z[i].y,&z[i].w);
}
work();
int ans=0;
for(int i=1;i<=q;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
a=a^ans;b^=ans;c^=ans;
a=find(a,b);
if(zz[root[rs[a]]].num-zz[root[ls[a]-1]].num<c)printf("-1\n"),ans=0;
else {
ans=hh[query(root[ls[a]-1],root[rs[a]],1,len,c)];
printf("%d\n",ans);
}
}
return 0;
}
P4768 [NOI2018] 归程
也是一道比较模板的题目吧
平平无奇的AC~
#include<bits/stdc++.h>
using namespace std;
#define ll long long
typedef pair<int,int> PII;
int n,m,Q,t,K,S;
struct node{
int x,y,w;
}z[400050];
bool cmp(node a,node b){
return a.w>b.w;
}
int len=0,head[400050],dis[400050],vis[400050];
priority_queue<PII,vector<PII>,greater<PII>> heap;
int cnt,ff[800050],fa[800050][22];
int len1=0,head1[800050];
int val[800060];
int ans[800060];
int find(int x){
if(x==ff[x])return x;
return ff[x]=find(ff[x]);
}
struct edge{
int e,w,nex;
}ed[800050],ed1[1600050];
void add(int a,int b,int l){
ed[len].e=b;
ed[len].w=l;
ed[len].nex=head[a];
head[a]=len++;
}
void add1(int a,int b){
ed1[len1].e=b;
ed1[len1].nex=head1[a];
head1[a]=len1++;
}
void init(){
len=0;len1=0;
for(int i=1;i<=n;i++)head[i]=-1,dis[i]=1e9+7,vis[i]=0;
for(int i=1;i<=2*n;i++)head1[i]=-1,val[i]=0;
}
void work1(){
dis[1]=0;
heap.push({0,1});
while(heap.size()){
PII t=heap.top();heap.pop();
int ver=t.second,dist=t.first;
if(vis[ver])continue;
vis[ver]=1;
for(int i=head[ver];i!=-1;i=ed[i].nex){
int j=ed[i].e;
if(dis[j]>dist+ed[i].w){
dis[j]=dist+ed[i].w;
heap.push({dis[j],j});
}
}
}
}
void dfs(int x,int pre){
fa[x][0]=pre;ans[x]=1e9+7;
for(int i=1;i<=20;i++)fa[x][i]=fa[fa[x][i-1]][i-1];
int f=0;
for(int i=head1[x];i!=-1;i=ed1[i].nex){
int to=ed1[i].e;
if(to==pre)continue;
dfs(to,x);
f=1;
ans[x]=min(ans[x],ans[to]);
}
if(!f)ans[x]=dis[x];
}
void work2(){
cnt=n;
for(int i=1;i<=n;i++)ff[i]=i;
sort(z+1,z+1+m,cmp);
for(int i=1;i<=m;i++){
int a=find(z[i].x);
int b=find(z[i].y);
if(a==b)continue;
val[++cnt]=z[i].w;
ff[cnt]=ff[a]=ff[b]=cnt;
add1(cnt,a);add1(a,cnt);
add1(cnt,b);add1(b,cnt);
}
dfs(cnt,0);
}
int find(int x,int y){
for(int i=20;i>=0;i--){
if(val[fa[x][i]]>y)x=fa[x][i];
}
return x;
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
init();
for(int i=1;i<=m;i++){
int a,b,c,d;
scanf("%d%d%d%d",&a,&b,&c,&d);
add(a,b,c);add(b,a,c);
z[i].x=a;z[i].y=b;z[i].w=d;
}
work1();
work2();
scanf("%d%d%d",&Q,&K,&S);
int res=0;
for(int i=1;i<=Q;i++){
int a,b;
scanf("%d%d",&a,&b);
a=(a+K*res-1)%n+1;
b=(b+K*res)%(S+1);
res=ans[find(a,b)];
printf("%d\n",res);
}
}
return 0;
}