Loading

kruskal 重构树学习笔记

目录

  1. 前言
  2. kruskal 最小生成树
  3. kruskal 重构树
    3.1 建树
    3.2 性质
  4. 应用

1.前言

应老师要求,来写一篇关于 kruskal 重构树的学习笔记

2. kruskal 最小生成树

在学重构树之前,我们需要了解 kruskal 最小生成树。
kruskal 最小生成树算法的流程为:将边从小到大排序,从小到大枚举每一条边,如果这条边连接的 2 个顶点没有联通,那么就将这条边加入最小生成树中。最后选出的边组成的树就为最小生成树。
由于算法很基础,所以不多赘述。

3. kruskal 重构树

3.1 建树

kruskal 重构树其实与最小生成树类似,但它不是将一条边加入一个边集,而是新建一个点,将边所连的 2 个点与新点相连。
举个例子:
对于这个无向图:
image
它的最小生成树是(红边):
image
但我们要建 kruskal 重构树,所以我们在 kruskal 建生成树的时候,每遍历到一条生成树中的边,我们遍新建一个节点 \(tot\) ,将这个边所连的两个点集与 \(tot\) 相连。这个点的点权记做这条边的边权。
图形形象理解:
还是这个无向图:
image
首先我们按 kruskal 的步骤来,排序后选出最小边:
image
建重构树(方点是新加的点):
image
注意:这个 5 不是它的点权,是编号。点权 w[5]=2(就是 \(1 \rightarrow 2\) 这条边的边权)
然后选出第二条边,建重构树:
image
同理,即可建出整棵重构树
image
如此。kruskal 重构树就建好了。

3.2 性质

  1. 重构树是节点总数为 \(2n-1\)二叉树
  2. 重构树是一个根堆(最小生成树:大根堆,最大生成树:小根堆)
  3. 按最小生成树重构的重构树中任意两点 \(a,b\) 的路径中的最大边权为它们的 LCA 的点权。也就是原图中,\(a,b\) 路径中最大边权的最小值 。最大生成树同理。
  4. 如果原图不连通,会得到重构树森林。
  5. 只有叶子节点是原图中的点

这些性质在处理一些题时很有用。

4. 应用

P2245 星际导航
先建出kruskal重构树,然后找 \(u,v\) 的LCA。该点点权即为答案
可以说是模板题了。
双倍经验:P1967 货车运输

#include<bits/stdc++.h>
#define ll long long
#define endl "\n"
using namespace std;
const int N=4e6+7;
int n,m;
inline int read() 
{
	register int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9') 
	{
		if(ch=='-') w=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9') 
	{
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*w;
}
struct node
{
	int to,nxt;
}e[N];
int head[N],cnt;
struct E
{
	int fr,to,w;
}edge[N];
int q;
bool cmp(E x,E y)
{
	return x.w<y.w;
}
void add(int u,int v)
{
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}
int fa[N],w[N];
int find(int x)
{
	if(x==fa[x]) return x;
	return fa[x]=find(fa[x]);
} 
int tot;
void kruskal()
{
	for(int i=1;i<=2*n;i++) fa[i]=i;
	for(int i=1;i<=m;i++)
	{
		int fau=find(edge[i].fr),fav=find(edge[i].to);
		if(fau==fav) continue;
		fa[fau]=++tot;
		fa[fav]=tot;
		w[tot]=edge[i].w;
		add(tot,fau);
		// add(fau,tot);
		add(tot,fav);
		// add(fav,tot);
		// cout<<tot<<" "<<fau<<" "<<fav<<endl;
	}
}
int f[N],siz[N],son[N],dfn[N],id[N],top[N],dep[N];
void dfs1(int x)
{
	siz[x]=1;
	int maxx=-1;
	for(int i=head[x];i;i=e[i].nxt)
	{
		int v=e[i].to;
		if(!dep[v])
		{
			dep[v]=dep[x]+1;
			f[v]=x;
			dfs1(v);
			siz[x]+=siz[v];
			if(siz[v]>maxx) maxx=siz[v],son[x]=v;
		}
	}
}
void dfs2(int x,int d)
{
	top[x]=d;
	if(!son[x]) return;
	dfs2(son[x],d);
	for(int i=head[x];i;i=e[i].nxt)
	{
		int v=e[i].to;
		if(dep[v]>dep[x]&&son[x]!=v) 
			dfs2(v,v); 
	}
}
inline int LCA(int x,int y)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		x=f[top[x]];
	}
	if(dep[x]<dep[y]) return x;
	return y;
}
int main()
{
	n=read();
	m=read();
	tot=n;
	for(int i=1;i<=m;i++)
	{
		edge[i].fr=read();
		edge[i].to=read();
		edge[i].w=read();
	}
	sort(edge+1,edge+1+m,cmp);
	kruskal();
	for(int i=tot;i>=1;i--)
	{
		if(!dep[i])
		{
			dfs1(i);
			dfs2(i,i);
		}
	}
	q=read();
	while(q--)
	{
		int u=read(),v=read();
		if(find(u)!=find(v)) 
		{
			cout<<"impossible"<<endl;
			continue;
		}
		int lca=LCA(u,v);
		// cout<<lca<<endl;
		cout<<w[lca]<<endl;
	}
	return 0;
}

P4768 [NOI2018] 归程
我们首先要求出每个点到 \(1\) 号点的最小花费 (dijkstra 最短路预处理) 。然后建出 kruskal 重构树,再维护以每个点作为根节点时子树中距离 1 号点的最小花费,直接 dfs 搞定。对于 u,我们直接在重构树上倍增即可。

#include<bits/stdc++.h>
#define ll long long
#define endl "\n"
using namespace std;
const int N=2e6+7;
struct node
{
	int fr,to,nxt,w,h;
}e[N],tr[N];
int vis[N],d[N],head[N];
priority_queue<pair<ll,int> > q;
int cnt=0;
int fa[N],dot[N];
int n,m,t;
void add(int u,int v,int w)
{
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	e[cnt].w=w;
	head[u]=cnt;
}
void dijkstra(int s)
{
    memset(vis,0,sizeof(vis));
    memset(d,0x3f,sizeof(d)); 
	d[s]=0;
    q.push(make_pair(0,s));
    while(!q.empty())
    {
        int u=q.top().second; q.pop();
        if(vis[u]) continue;
        vis[u]=1;
        for(int i=head[u];i;i=e[i].nxt)
        {
            int v=e[i].to;
            if(d[u]+e[i].w<d[v])
            {
                d[v]=d[u]+e[i].w;
                q.push(make_pair(-d[v],v));
            }
        }
    }
}
bool cmp(node x,node y){return x.h>y.h;}
int find(int x)
{
	if(fa[x]==x) return fa[x];
	return fa[x]=find(fa[x]);
}
int tot;
int minn[N];
int bz[N][24];
void dfs(int x)
{
	minn[x]=d[x];
	for(int i=head[x];i;i=e[i].nxt)
	{
		bz[e[i].to][0]=x;
		dfs(e[i].to);
		minn[x]=min(minn[x],minn[e[i].to]);
	}
}
void kruskra()
{
	sort(tr+1,tr+1+m,cmp);
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=1;i<=m;i++)
	{
		int fau=find(tr[i].fr),fav=find(tr[i].to);
		if(fau==fav) continue;
		dot[++tot]=tr[i].h;
		add(tot,fau,0);
		add(tot,fav,0);
		fa[fau]=tot;
		fa[fav]=tot;
		fa[tot]=tot;
	}
}

int main()
{
	cin>>t;
	
	while(t--)
	{
		memset(vis,0,sizeof(vis));
		memset(head,0,sizeof(head));
		cin>>n>>m;
		for(int i=1;i<=m;i++)
		{
			int a,b,c,d;
			cin>>a>>b>>c>>d;
			add(a,b,c);
			add(b,a,c);
			tr[i].fr=a,tr[i].to=b;
			tr[i].h=d;
		}
		dijkstra(1);
		tot=n;
		cnt=0;
		memset(head,0,sizeof(head));
		kruskra();
		memset(minn,0x3f,sizeof(minn));
		memset(bz,0,sizeof(bz));
		dfs(tot);
		for(int i=1;(1<<i)<=tot;i++)
        	for(int j=1;j<=tot;j++)
        		bz[j][i]=bz[bz[j][i-1]][i-1];
		int q,k,s;
		cin>>q>>k>>s;
		int lastans=0;
		while(q--)
		{
			int v0,p0;
			cin>>v0>>p0;
			int v=(v0+k*lastans-1)%n+1;
			int p=(p0+k*lastans)%(s+1);
			for(int i=22;i>=0;i--)
				if(bz[v][i]&&dot[bz[v][i]]>p)
					v=bz[v][i];
			cout<<minn[v]<<endl;
			lastans=minn[v];
		}
	}	
	return 0;
}

P4197 Peaks
先建出重构树,然后建出主席树,对于每个询问,答案是以 \(x\) 为根节点的子树内的第 \(k\)
对于求 \(x\) ,就是前面讲的。

#include<bits/stdc++.h>
#define ll long long
#define endl "\n"
using namespace std;
const int N=2e6+7;
struct node
{
    int fr,to,nxt,w;
}edge[N],e[N];
int head1[N],cnt1;
int h[N],hh[N];
struct treee
{
    int l,r,w,L,R;
}t[N*4],tt[N*4];
void add1(int u,int v,int w)
{
    edge[++cnt1].to=v;
    edge[cnt1].fr=u;
    edge[cnt1].nxt=head1[u];
    edge[cnt1].w=w;
    head1[u]=cnt1;
}
int tot,cnt;
bool cmp(node a,node b)
{
    return a.w<b.w;
}
int fa[N];
int n,m,q;
int f[N][32];
int weight[N];
int rot[N];
void dfs(int x,int fat)
{
    // cout<<x<<endl;
    f[x][0]=fat;
    for(int i=1;i<=20;i++)
        f[x][i]=f[f[x][i-1]][i-1];
    if(!t[x].w)
    {
        weight[++cnt]=h[x];
        // cout<<weight[cnt]<<" "<<cnt<<endl;
        t[x].L=t[x].R=cnt;
        return ;
    }
    dfs(t[x].l,x);
    dfs(t[x].r,x);
    t[x].L=t[t[x].l].L;
    t[x].R=t[t[x].r].R;
}
int find(int x)
{
    if(x==fa[x])
        return x;
    return fa[x]=find(fa[x]);
}
void kruskal()
{
    // cout<<tot<<endl;
    sort(edge+1,edge+1+m,cmp);
    for(int i=1;i<=m;i++)
    {
        int fau=find(edge[i].fr),fav=find(edge[i].to);
        // cout<<i<<endl;
        if(fau==fav) continue;
        // cout<<edge[i].fr<<"a "<<edge[i].to<<"a "<<edge[i].w<<endl;
        ++tot;
        fa[fau]=fa[fav]=tot;
        t[tot].l=fau;
        t[tot].r=fav;
        t[tot].w=edge[i].w;
        // cout<<tot<<" a"<<t[tot].w<<endl;
        
    }
    // cout<<tot<<endl;
}
int build(int l,int r)
{
    int now=++cnt;
    tt[now].w=0;
    if(l==r)
        return now;
    int mid=(l+r)/2;
    tt[now].l=build(l,mid);
    tt[now].r=build(mid+1,r);
    return now;
}
int add(int x,int l,int r,int k,int val)
{
    int now=++cnt;
    tt[now].l=tt[x].l;
    tt[now].r=tt[x].r;
    tt[now].w=tt[x].w;
    if(l==r)
    {
        tt[now].w+=val;
        return now;
    }
    int mid=(l+r)/2;
    if(k<=mid) tt[now].l=add(tt[now].l,l,mid,k,val);
    else tt[now].r=add(tt[now].r,mid+1,r,k,val);
    tt[now].w=tt[tt[now].l].w+tt[tt[now].r].w;
    return now;
}
int query(int L,int R,int l,int r,int k)
{
    if(l==r) return l;
    int val=tt[tt[L].l].w-tt[tt[R].l].w;
    int mid=(l+r)/2;
    if(val>=k) return query(tt[L].l,tt[R].l,l,mid,k);
    else return query(tt[L].r,tt[R].r,mid+1,r,k-val);
}
int main()
{
    cin>>n>>m>>q;
    for(int i=1;i<=n;i++) 
    {
        cin>>h[i];
        hh[i]=h[i];
    }
    sort(hh+1,hh+1+n);
    int loon=unique(hh+1,hh+1+n)-hh-1;
    for(int i=1;i<=n;i++)
        h[i]=lower_bound(hh+1,hh+1+loon,h[i])-hh;
    for(int i=1;i<=m*2;i++) fa[i]=i;
    for(int i=1;i<=m;i++)
    {
        int a,b,c;
        cin>>a>>b>>c;
        add1(a,b,c);
    }
    tot=n;
    kruskal();
    dfs(tot,0);
    rot[0]=build(1,n);
    for(int i=1;i<=n;i++)
        rot[i]=add(rot[i-1],1,n,weight[i],1);
    while(q--)
    {
        int v,x,k;
        cin>>v>>x>>k;
        for(int i=20;i>=0;i--)
            if(t[f[v][i]].w<=x&&f[v][i])
                v=f[v][i];
        int len=t[v].R-t[v].L+1;
        if(len<k)
            cout<<-1<<endl;
        else
        { 
            k=len-k+1;
            int way=query(rot[t[v].R],rot[t[v].L-1],1,n,k);
            cout<<hh[way]<<endl;
        }
    }
    return 0;
}
/*
5 4 5

1 2 3 4 5

1 2 5
2 3 5
3 4 5
4 5 5

1 5 3
2 5 5
3 5 4
4 5 2
5 5 1
*/

P4899 [IOI2018] werewolf 狼人
先以节点编号大小建 2 棵重构树,然后建出树状数组,对于每个询问,就是在 \(dfn[L[i]]\)\(dfn[R[i]]\) 间数点

#include<bits/stdc++.h>
#define ll long long 
using namespace std;
int n,m,q;
const int N=1e6+7;
struct node
{
    int to,nxt,w,fr;
}e1[N],e2[N],e[N];//e1 Öع¹Ê÷1  e2 Öع¹Ê÷ 2   e ԭͼ
int head1[N],head2[N],cnt1,cnt2,cnt;
void add1(int u,int v)//µÚÒ»¿Ã
{
    e1[++cnt1].to=v;
    e1[cnt1].nxt=head1[u];
    head1[u]=cnt1;
}
void add2(int u,int v)//µÚ¶þ¿Ã   
{
    e2[++cnt2].to=v;
    e2[cnt2].nxt=head2[u];
    head2[u]=cnt2;
}
int fa1[N],fa2[N];
bool cmp1(node a,node b)
{
    return a.w>b.w;
}
bool cmp2(node a,node b)
{
    return a.w<b.w;
}
int find1(int x)//1
{
    if(x==fa1[x]) return x;
    return fa1[x]=find1(fa1[x]);
}
int find2(int x)//2
{
    if(x==fa2[x]) return x;
    return fa2[x]=find2(fa2[x]);
}
int tot1,tot2;//1,2Öع¹Ê÷µÄµãÊý
int w1[N],w2[N];
void kruskal1()//µÚÒ»¿Ã
{
    sort(e+1,e+1+m,cmp1);
    for(int i=1;i<=m;i++)
    {
        int fau=find1(e[i].fr),fav=find1(e[i].to);
        if(fau==fav) continue;
        tot1++;
        add1(tot1,fau);
        add1(tot1,fav);
        w1[tot1]=e[i].w;
        fa1[fav]=tot1;
        fa1[fau]=tot1;
        fa1[tot1]=tot1;
    }
}
void kruskal2()//µÚ¶þ¿Ã
{
    sort(e+1,e+1+m,cmp2);
    for(int i=1;i<=m;i++)
    {
        int fau=find2(e[i].fr),fav=find2(e[i].to);
        if(fau==fav) continue;
        tot2++;
        add2(tot2,fau);
        add2(tot2,fav);
        w2[tot2]=e[i].w;
        fa2[fav]=tot2;
        fa2[fau]=tot2;
        fa2[tot2]=tot2;
    }
}
int dfn1[N],dfn2[N],low1[N],low2[N],c1,c2;//1 µÚÒ»¸öÖع¹Ê÷£¬2 µÚ¶þ¸öÖع¹Ê÷
int f1[N][21],f2[N][21];
void dfs1(int x,int fa)
{
    dfn1[x]=++c1;
    f1[x][0]=fa;
    for(int i=1;i<=20;i++)
    {
        f1[x][i]=f1[f1[x][i-1]][i-1];
    }
    for(int i=head1[x];i;i=e1[i].nxt)
    {
        int v=e1[i].to;
        if(v==fa) continue;
        dfs1(v,x);
    }
    low1[x]=c1;
}
void dfs2(int x,int fa)
{
    dfn2[x]=++c2;
    f2[x][0]=fa;
    for(int i=1;i<=20;i++)
        f2[x][i]=f2[f2[x][i-1]][i-1];
    for(int i=head2[x];i;i=e2[i].nxt)
    {
        int v=e2[i].to;
        if(v==fa) continue;
        dfs2(v,x);
    }
    low2[x]=c2;
}
int query1(int x,int k)
{
    for(int i=20;i>=0;i--)
        if(f1[x][i]&&w1[f1[x][i]]>=k)
            x=f1[x][i];
    return x;
}
int query2(int x,int k)
{
    for(int i=20;i>=0;i--)
        if(f2[x][i]&&w2[f2[x][i]]<=k)
            x=f2[x][i];
    return x;
}
int t[N*4];
int lowbit(int x)
{
    return x&(-x);
}
int tot=0;//Ê÷×´Êý×éÊýµã
void add(int x,int k)
{
    for(;x<=tot;x+=lowbit(x))
        t[x]+=k;
}
int sum(int x)
{
    int ans=0;
    for(;x;x-=lowbit(x))
        ans+=t[x];
    return ans;
}
struct dot
{
    int x,y,id,d;
}a[N];
inline bool cmp(dot x,dot y)
{
    if(x.x==y.x)
    {
        if(x.y==y.y)
            return x.id<y.id;
        return x.y<y.y;
    }
    return x.x<y.x;
}
int fin[N][5];
int main()
{
    cin>>n>>m>>q;
    for(int i=1;i<=n;i++)
        fa1[i]=fa2[i]=i;
    tot1=tot2=n;
    for(int i=1;i<=m;i++)
    {
        int u,v;
        cin>>u>>v;
        u++;v++;
        e[++cnt].fr=u;
        e[cnt].to=v;
        e[cnt].w=min(u,v);
    }
    kruskal1();
    for(int i=1;i<=m;i++)
        e[i].w=max(e[i].fr,e[i].to);
    kruskal2();
    dfs1(tot1,tot1);
    dfs2(tot2,tot2);
    for(int i=1;i<=n;i++)
        a[++tot]=(dot){dfn1[i],dfn2[i],0,0};
//    for(int i=1;i<=tot1;i++) cout<<dfn1[i]<<" "<<low1[i]<<"  ";
//    cout<<endl;
//    for(int i=1;i<=tot2;i++) cout<<dfn2[i]<<" "<<low2[i]<<"  ";
    for(int i=1;i<=q;i++)
    {
        int st,en,l,r;
        cin>>st>>en>>l>>r;
        st++;en++;l++;r++;
        st=query1(st,l);
        en=query2(en,r);
        int x1=dfn1[st],x2=low1[st],y1=dfn2[en],y2=low2[en];
        a[++tot]=(dot){x1-1,y2,i,1};
        a[++tot]=(dot){x2,y2,i,2};
        a[++tot]=(dot){x2,y1-1,i,3};
        a[++tot]=(dot){x1-1,y1-1,i,4};
    }
    sort(a+1,a+1+tot,cmp);
//	cout<<tot<<endl;
    for(int i=1;i<=tot;i++)
    {
//		cout<<a[i].x<<" "<<a[i].y<<" "<<a[i].id<<" "<<a[i].d<<endl;
        if(a[i].id==0) {add(a[i].y,1);}
        else fin[a[i].id][a[i].d]+=sum(a[i].y);
    }
//	for(int i=1;i<=tot1;i++)
//		cout<<sum(i)<<endl;
    for(int i=1;i<=q;i++)
    {
        int val=fin[i][2]-fin[i][1]-fin[i][3]+fin[i][4];
        if(val>0) cout<<1<<endl;
        else cout<<0<<endl;
    }
    return 0;
}
posted @ 2022-09-28 10:56  sheeplittlecloud  阅读(82)  评论(0编辑  收藏  举报