Nowcoder9986H.动态最小生成树(线段树+Krustral算法+归并排序)

牛客暑期集训第六场题解

zhanglichen

H.动态最小生成树

题意:

小Z喜欢最小生成树。

小Z有一张n*m的图,请你支持两种操作:

(1)修改第x条边为连接点y,z,边权为t。

(2)查询只用编号在[l,r]范围内的边,得到的最小生成树权值是多少。

题解:

猜想一个性质:

对于两个边集A和B,这两个边集合并后的边集C的最小生成树可以直接由A的最小生成树和B的最小生成树合并得到(我不会证)。

观察到点数只有200,考虑用线段树维护,线段树上每个节点维护对应区间内的边集涉及的点集的最小生成树。

线段树pushup的时候,不用排序,定义两个指针x和y分别表示左区间的遍历位置和右区间的遍历位置,采用有序链表合并的方式合并两个区间。

注意:father数组一定要定义在外面,因为程序在同一时间只能运行一层递归函数,只需要每次合并区间的时候初始化一下father[i]即可,如果每个节点都定义一个father数组,会导致超时。

时间复杂度\(O(200nlogn)\)

// Problem: 动态最小生成树
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/9986/H
// Memory Limit: 1048576 MB
// Time Limit: 10000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

//200个点,30000条边
//30000个询问
//对边分块
//每一块的边所包含的点集的最小生成树
//合并点集
//枚举两块边的每条生成树上的边
//取小的边作为新的生成树

//单点修改

#include<bits/stdc++.h>
using namespace std;
const int maxn=3e4+100;
int n,m,q;



struct edge {
	int u,v,w;
	bool operator < (const edge &r) const {
		return w<r.w;
	}
}e[maxn];
int father[205];

struct node {
	int l,r;
	vector<int> p;
	long long sum=0;
}segTree[maxn<<2];


int findfather (int x) {
	int a=x;
	while (x!=father[x]) x=father[x];
	while (a!=father[a]) {
		int z=a;
		a=father[a];
		father[z]=x;
	}
	return x;
}

void build (int i,int l,int r) {
	segTree[i].l=l;
	segTree[i].r=r;
	if (l==r) {
		segTree[i].p.push_back(l);
		segTree[i].sum=e[l].w;
		return;
	}
	int mid=(l+r)>>1;
	build(i<<1,l,mid);
	build(i<<1|1,mid+1,r);
	
	for (int j=1;j<=n;j++)father[j]=j;segTree[i].sum=0;segTree[i].p.clear();
	int x=0,y=0;
	while (x<segTree[i<<1].p.size()||y<segTree[i<<1|1].p.size()) {
		if (x<segTree[i<<1].p.size()&&y<segTree[i<<1|1].p.size()) {
			if (e[segTree[i<<1].p[x]].w<e[segTree[i<<1|1].p[y]].w) {
				int u=findfather(e[segTree[i<<1].p[x]].u);
				int v=findfather(e[segTree[i<<1].p[x]].v);
				if (u==v) {
					x++;
					continue;
				}
				segTree[i].p.push_back(segTree[i<<1].p[x]);
				father[u]=v;
				segTree[i].sum+=e[segTree[i<<1].p[x]].w;
				x++;
			} 
			else {
				int u=findfather(e[segTree[i<<1|1].p[y]].u);
				int v=findfather(e[segTree[i<<1|1].p[y]].v);
				if (u==v) {
					y++;
					continue;
				}
				segTree[i].p.push_back(segTree[i<<1|1].p[y]);
				father[u]=v;
				segTree[i].sum+=e[segTree[i<<1|1].p[y]].w;
				y++;
			}
		}
		else if (x<segTree[i<<1].p.size()) {
			int u=findfather(e[segTree[i<<1].p[x]].u);
				int v=findfather(e[segTree[i<<1].p[x]].v);
				if (u==v) {
					x++;
					continue;
				}
				segTree[i].p.push_back(segTree[i<<1].p[x]);
				father[u]=v;
				segTree[i].sum+=e[segTree[i<<1].p[x]].w;
				x++;
		}
		else {
			int u=findfather(e[segTree[i<<1|1].p[y]].u);
				int v=findfather(e[segTree[i<<1|1].p[y]].v);
				if (u==v) {
					y++;
					continue;
				}
				segTree[i].p.push_back(segTree[i<<1|1].p[y]);
				father[u]=v;
				segTree[i].sum+=e[segTree[i<<1|1].p[y]].w;
				y++;
		}
	}
}
void up (int i,int pp) {
	if (segTree[i].l==pp&&segTree[i].r==pp) {
		return;
	}
	int mid=(segTree[i].l+segTree[i].r)>>1;
	if (pp<=mid) up(i<<1,pp);
	if (pp>mid) up(i<<1|1,pp);
	
	for (int j=1;j<=n;j++)father[j]=j;segTree[i].sum=0;segTree[i].p.clear();
	int x=0,y=0;
	while (x<segTree[i<<1].p.size()||y<segTree[i<<1|1].p.size()) {
		if (x<segTree[i<<1].p.size()&&y<segTree[i<<1|1].p.size()) {
			if (e[segTree[i<<1].p[x]].w<e[segTree[i<<1|1].p[y]].w) {
				int u=findfather(e[segTree[i<<1].p[x]].u);
				int v=findfather(e[segTree[i<<1].p[x]].v);
				if (u==v) {
					x++;
					continue;
				}
				segTree[i].p.push_back(segTree[i<<1].p[x]);
				father[u]=v;
				segTree[i].sum+=e[segTree[i<<1].p[x]].w;
				x++;
			} 
			else {
				int u=findfather(e[segTree[i<<1|1].p[y]].u);
				int v=findfather(e[segTree[i<<1|1].p[y]].v);
				if (u==v) {
					y++;
					continue;
				}
				segTree[i].p.push_back(segTree[i<<1|1].p[y]);
				father[u]=v;
				segTree[i].sum+=e[segTree[i<<1|1].p[y]].w;
				y++;
			}
		}
		else if (x<segTree[i<<1].p.size()) {
			int u=findfather(e[segTree[i<<1].p[x]].u);
				int v=findfather(e[segTree[i<<1].p[x]].v);
				if (u==v) {
					x++;
					continue;
				}
				segTree[i].p.push_back(segTree[i<<1].p[x]);
				father[u]=v;
				segTree[i].sum+=e[segTree[i<<1].p[x]].w;
				x++;
		}
		else {
			int u=findfather(e[segTree[i<<1|1].p[y]].u);
				int v=findfather(e[segTree[i<<1|1].p[y]].v);
				if (u==v) {
					y++;
					continue;
				}
				segTree[i].p.push_back(segTree[i<<1|1].p[y]);
				father[u]=v;
				segTree[i].sum+=e[segTree[i<<1|1].p[y]].w;
				y++;
		}
	}
}

node query (int i,int l,int r) {
	if (segTree[i].l>=l&&segTree[i].r<=r) {
		return segTree[i];
	}
	int mid=(segTree[i].l+segTree[i].r)>>1;
	vector<edge> pp;
	node ans;
	node t1;
	node t2;
	int x=0,y=0;
	if (l<=mid) t1=query(i<<1,l,r);
	if (r>mid) t2=query(i<<1|1,l,r);
	for (int j=1;j<=n;j++) father[j]=j;
	ans.sum=0;
	while (x<t1.p.size()||y<t2.p.size()) {
		if (x<t1.p.size()&&y<t2.p.size()) {
			if (e[t1.p[x]].w<e[t2.p[y]].w) {
				int u=findfather(e[t1.p[x]].u);
				int v=findfather(e[t1.p[x]].v);
				if (u==v) {
					x++;
					continue;
				}
				ans.p.push_back(t1.p[x]);
				father[u]=v;
				ans.sum+=e[t1.p[x]].w;
				x++;
			} 
			else {
				int u=findfather(e[t2.p[y]].u);
				int v=findfather(e[t2.p[y]].v);
				if (u==v) {
					y++;
					continue;
				}
				ans.p.push_back(t2.p[y]);
				father[u]=v;
				ans.sum+=e[t2.p[y]].w;
				y++;
			}
		}
		else if (x<t1.p.size()) {
			int u=findfather(e[t1.p[x]].u);
				int v=findfather(e[t1.p[x]].v);
				if (u==v) {
					x++;
					continue;
				}
				ans.p.push_back(t1.p[x]);
				father[u]=v;
				ans.sum+=e[t1.p[x]].w;
				x++;
		}
		else {
			int u=findfather(e[t2.p[y]].u);
				int v=findfather(e[t2.p[y]].v);
				if (u==v) {
					y++;
					continue;
				}
				ans.p.push_back(t2.p[y]);
				father[u]=v;
				ans.sum+=e[t2.p[y]].w;
				y++;
		}
	}
	return ans;
}
int main () {
	scanf("%d%d%d",&n,&m,&q);
	for (int i=1;i<=m;i++) {
		scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
	}
	build(1,1,m);
	while (q--) {
		int op;
		scanf("%d",&op);
		if (op==1) {
			int x,y,z,t;
			scanf("%d%d%d%d",&x,&y,&z,&t);
			e[x].u=y;e[x].v=z;e[x].w=t;
			up(1,x);
		}
		else {
			int l,r;
			scanf("%d%d",&l,&r);
			node ans=query(1,l,r);
			int f=1;
			for (int i=1;i<=n;i++) if (findfather(i)!=findfather(1)) f=0;
			if (!f) {
				printf("Impossible\n");
				continue;
			}
			printf("%lld\n",ans.sum);
		}
	}
}


posted @ 2021-03-11 22:44  zlc0405  阅读(262)  评论(0编辑  收藏  举报