Kruskal

Kruscal最小生成树算法,将边按照权值进行排序,每次贪心优先选择权值较小的边,并依次连接,若出现环则跳过,边数达到节点数-1时即可停止。时间复杂度O(Mlog2(M))

inline int kruskal(){/*最小生成树用于无向图,连边时可以按照单向边来连*/
	sort(e+1,e+1+tot);/*按照边权升序排序*/
    d.init(n);
	int ans=0,cnt=0;
	for(int i=1;i<=tot;i++){
		int x=e[i].from,y=e[i].to;
		if(d.merge(x,y))ans+=e[i].w,cnt++;/*并查集维护,尚未成环则加入生成树,统计生成树中边的数量*/
		if(cnt==n-1)break;/*终止条件*/
	}
	if(cnt!=n-1)return false;/*图不连通*/
	return ans;
}

Kruscal重构树是在进行Kruscal算法时,对于每次选择的边新建一个节点,点权赋值为所选择边的边权,该点向边的两个端点连边。重构树的n个叶子为原图中的n个节点且没有点权,重构树满足大根堆或小根堆性质,x和y点的lca的点权就对应着它们最小生成树上的瓶颈。

inline void exkruscal(){
	int tot=0,cnt=n;
	d.init(n<<1);
	sort(e+1,e+1+m);
	for(int i=1;i<=m;i++){
		int x=d.find(e[i].x),y=d.find(e[i].y);
		if(x==y)continue;
		++tot;
		add(++cnt,x);
		add(cnt,y);
		d.f[x]=d.f[y]=cnt;
		p[cnt].a=e[i].a;
		if(tot==n-1)break;
	}
	dfs(cnt,0);
}

一张无向图,每次询问一个点对[l,r]表示对于区间内的所有点,求一个k使得只走前k条边,都可以互相到达,前k条边指的是第1...k条边。将边的编号转化成看作边权,可以转化成kruscal重构树,几个点要互相到达,最小化经过的所有边权的最大值,对于求区间[l,r]的LCA,取其中dfs序最大和最小两者即可。

        d.init(n<<1);
        int tot=dfn=0,cnt=n;
        for(int i=1;i<=m;i++){
            int x,y;
            cin>>x>>y;
            x=d.find(x),y=d.find(y);
            if(x!=y&&tot<=n-2){
                tot++;
                p[++cnt]={x,y};
                val[cnt]=i;
                d.f[x]=d.f[y]=cnt;
            }
        }
        dfs(cnt,0);
        STTable();
        while(q--){
            int l,r;
            cin>>l>>r;
            cout<<val[LCA(mp[query(l,r,0)],mp[query(l,r,1)])]<<' ';
        }

归程。

#include<bits/stdc++.h>

using namespace std;

const int N=4e5+5;
int n,m,q,s,k,h0[N<<1],h[N],cntx,dis[N],dep[N],f[N][25],totx;
bool vis[N];
struct Node{
	int x,y,l,a;
	inline bool friend operator<(const Node&x,const Node&y){
		return x.a>y.a;
	}
}e[N<<2],p[N<<1];
struct edge{
	int to,next;
}tr[N<<2];
struct node{
	int to,next,w;
}t[N<<1];
struct DSU{
	int f[N<<1];
	inline void init(int n){
		for(int i=1;i<=n;i++)f[i]=i;
	}
	int find(int x){
		return x==f[x]?x:f[x]=find(f[x]);
	}
}d;
inline void dijkstra(int s){
    priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>>q;
    memset(vis,0,sizeof(vis));
    memset(dis,0x3f,sizeof(dis));
    q.push({0,s});
    dis[s]=0;
    while(!q.empty()){
        int x=q.top().second;
        q.pop();
        if(vis[x])continue;
        vis[x]=1;
        for(int i=h[x];i;i=t[i].next){
            int y=t[i].to;
            if(dis[y]>dis[x]+t[i].w){
                dis[y]=dis[x]+t[i].w;
                q.push({dis[y],y});
            }
        }
    }
	for(int i=1;i<=n;i++)p[i].l=dis[i];
}
inline void add(int x,int y){
	tr[++cntx]={y,h0[x]},h0[x]=cntx;
}
inline void addx(int x,int y,int w){
	t[++totx]={y,h[x],w},h[x]=totx;
}
void dfs(int x,int fa){
	dep[x]=dep[fa]+1;
	f[x][0]=fa;
	for(int i=1;i<=20;i++)f[x][i]=f[f[x][i-1]][i-1];
	for(int i=h0[x];i;i=tr[i].next){
		int y=tr[i].to;
		dfs(y,x);
		p[x].l=min(p[x].l,p[y].l);
	}
}
inline int query(int x,int y){
	for(int i=20;~i;i--)if(dep[x]-(1<<i)>0&&p[f[x][i]].a>y)x=f[x][i];
	return p[x].l;
}
inline void exkruscal(){
	int tot=0,cnt=n;
	d.init(n<<1);
	sort(e+1,e+1+m);
	for(int i=1;i<=m;i++){
		int x=d.find(e[i].x),y=d.find(e[i].y);
		if(x==y)continue;
		++tot;
		add(++cnt,x);
		add(cnt,y);
		d.f[x]=d.f[y]=d.f[cnt]=cnt;
		p[cnt].a=e[i].a;
		if(tot==n-1)break;
	}
	dfs(cnt,0);
}
int main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int t;cin>>t;
	while(t--){
		int last=0;
		cin>>n>>m;
		memset(e,0,sizeof(e));
		cntx=totx=0;
		memset(h0,0,sizeof(h0));
		memset(h,0,sizeof(h));
		memset(f,0,sizeof(f));
		for(int i=1;i<=m;i++){
			int x,y,l,a;
			cin>>x>>y>>l>>a;
			e[i]={x,y,l,a};
			addx(x,y,l),addx(y,x,l);
		}
		for(int i=n+1;i<=(n<<1);i++)p[i].l=0x3f3f3f3f;
		dijkstra(1);
		cin>>q>>k>>s;
		exkruscal();
		while(q--){
			int x,y;
			cin>>x>>y;
			x=(k*last+x-1)%n+1,y=(k*last+y)%(s+1);
			cout<<(last=query(x,y))<<'\n';
		}
	}
	return 0;
}

一张图每条边有两个属性gi和si,求一个生成树,是的gmax(g[i])+smax(s[i])最小。首先按照g升序,依次插入每条边,每插入一条边就将其按照s升序,之后构建最小生成树,每次不用重新排序,将新插入的边和之前边进行比较交换即可,由于之前用不到的边以后也不会用,可以记录上一次用到的边和边数。

    sort(e+1,e+1+m);
    for(int i=1;i<=m;i++){
        a[++tot]=e[i];
        for(int j=tot-1;j;j--)if(a[j].s>a[j+1].s)swap(a[j],a[j+1]);
        d.init(n);
        int cnt=0,gg=0,ss=0;
        for(int i=1;i<=tot;i++){
            int x=d.find(a[i].x),y=d.find(a[i].y);
            if(x==y)continue;
            d.merge(x,y);
            a[++cnt]=a[i];
            gg=max(gg,a[i].g),ss=max(ss,a[i].s);
        }
        if(cnt==n-1)ans=min(ans,g*gg+s*ss);
        tot=cnt;
    }

求严格次小生成树。严格次小生成树相比最小生成树肯定改变且仅改变了一条边,在建出kruskal生成树之后,枚举不在树上的每一条边添加,那么肯定形成一个环,且环上任意一条边的边权都不大于加入的边的边权,在环上找到严格小于加入的边的边权的边并将其删去,之后更新答案。倍增维护最大值和次大值,由于环上边权均不大于加入的边的边权,所以当加入的边的边权和环上最大值相等时,环上次大值即为严格小于加入的边的边权的边权,否则取环上最大值。

inline void kruskal(){
    sort(edge+1,edge+1+m);
    dsu.init(n);
    int tot=0;
    for(int i=1;i<=m;i++){
        int x=dsu.find(edge[i].x),y=dsu.find(edge[i].y);
        if(x==y)continue;
        dsu.merge(x,y);
        sum+=edge[i].w;
        add(edge[i].x,edge[i].y,edge[i].w);
        add(edge[i].y,edge[i].x,edge[i].w);
        if(++tot==n-1)break;
    }
}
void dfs(int x,int f){
    dep[x]=dep[f]+1;
    fa[x][0]=f;
    for(int i=1;i<=18;i++){
        int f=fa[x][i-1];
        fa[x][i]=fa[f][i-1];
        if(ma[x][i-1][0]==ma[f][i-1][0]){//两段最大值相等
            ma[x][i][0]=ma[x][i-1][0];//直接赋值
            ma[x][i][1]=max(ma[x][i-1][1],ma[f][i-1][1]);//次大值在两段中取max
        }
        else if(ma[x][i-1][0]>ma[f][i-1][0]){//左边较大
            ma[x][i][0]=ma[x][i-1][0];//最大值选左边
            ma[x][i][1]=max(ma[x][i-1][1],ma[f][i-1][0]);//右边最大值有可能成为次大值
        }
        else{//右边较大
            ma[x][i][0]=ma[f][i-1][0];//最大值选右边
            ma[x][i][1]=max(ma[x][i-1][0],ma[f][i-1][1]);//左边最大值有可能成为次大值
        }
    }
    for(int i=h[x];i;i=e[i].next){
        int y=e[i].to;
        if(y==f)continue;
        ma[y][0][0]=e[i].w;//边权最大值
        ma[y][0][1]=-1;//边权次大值
        dfs(y,x);
    }
}
inline void cmax(int&x,int w,int a[2]){x=max(x,a[w==a[0]]);}
inline int LCA(int x,int y,int w){
    int re=-1;
    if(dep[x]<dep[y])swap(x,y);
    for(int i=18;~i;i--)if(dep[fa[x][i]]>=dep[y])cmax(re,w,ma[x][i]),x=fa[x][i];
    if(x==y)return re;
    for(int i=18;~i;i--){
        if(fa[x][i]!=fa[y][i]){
            cmax(re,w,ma[x][i]);
            cmax(re,w,ma[y][i]);
            x=fa[x][i];
            y=fa[y][i];
        }
    }
    cmax(re,w,ma[x][0]);
    cmax(re,w,ma[y][0]);
    return re;
}
    for(int i=1;i<=m;i++){
        if(vis[i])continue;
        int re=LCA(edge[i].x,edge[i].y,edge[i].w);
        if(re==-1)continue;
        ans=min(ans,sum-re+edge[i].w);
    }
posted @ 2022-11-14 17:44  半步蒟蒻  阅读(85)  评论(0)    收藏  举报