2020杭电多校2 1001 Total Eclipse

题目

2020杭电多校2 1001 Total Eclipse_第1张图片
题目链接

题目大意就是在一张图上,有n个节点,m条无向边。每个节点有一个亮度值,每次可以选择一个连通块,块中所有节点值-1.问多少次操作可以使得图上所有节点的点权为0.

排序+并查集

需要注意的是,已经成为0的点不能再被选入连通块中,否则就成为负数了。因此当一个点其点权成为0的时候就相当于点本身及以其为顶点的边被删除了,不能对后面的过程产生影响。

仔细分析一下,整个过程就是不断的挑选连通块,操作其中点权最小值次,过后把点权为0的点删除,形成更多的连通块,往复这个过程,知道所有的点点权都为0.

一种可行的思路是这样:反向执行上述过程,将所有点排序,从点权大的点开始遍历起,使其不断连接不同的连通块(仅考虑边终点权值比当前点小,即仅考虑从小点向大点连接)。

当其连接上一个连通块时,对答案的贡献就是该连通块中点权的最小值与当前点点权的差。维护连通块可使用并查集来解决

答案统计除去在合并时产生的部分,还需要在最后加上所有连通块中最小点权的值。

代码:

#include
#include
#include
#define N 100050
#define M 200050
using namespace std;
struct V{
	int x;
	int next;
}nxt[M];

struct P{
	int w;
	int code;
	int head;
	
	bool operator < (const P& a) const{
		return w < a.w;
	}
	
	bool operator <= (const P& a) const{
		return w <= a.w;
	}
}point[N];
int fa[N];
int cToP[N];
int n,T,m;
int tot;
long long ans;

/*连接两个点,x->y*/
void link(int x,int y){
	nxt[++tot].x = y;
	nxt[tot].next = point[x].head;
	point[x].head = tot;
}

bool cmp(const P&a,const P&b){
	return b < a;
}

int find(int k){
	if(fa[k] == k)
		return k;
	fa[k] = find(fa[k]);
	return fa[k];
}

int main(){
	for(cin >> T;T != 0;T--){
		//读入nm 
//		cin >> n >> m;
		scanf("%d%d",&n,&m);
		
		//初始化边总数和答案 
		tot = 0;
		ans = 0;
		
		//读入点数据 
		for(int i = 1;i <= n;i++){
			
//			cin >> point[i].w;
			scanf("%d",&point[i].w);
			point[i].code = i;
			point[i].head = 0; 
			fa[i] = i;
		}
		
		//读入边数据
		//小的指向大的,相等互相指 
		for(int i = 1,x,y;i <= m;i++){
//			cin >> x >> y;
			scanf("%d%d",&x,&y);
			if(point[x] <= point[y]){
				link(x,y);
			}
			if(point[y] <= point[x]){
				link(y,x);
			}
		} 
		
		/*给点按照权重排序,降序*/
		sort(point + 1,point + 1 + n,cmp);
		/*code序号对应排序后的位置*/
		for(int i = 1;i <= n;i++){
			cToP[point[i].code] = i;
		}
		
		/*for(int i = 1;i <= n;i++){
			cout << point[i].code << " " << point[i].w << endl;
		}*/
		P p,q;
		for(int i = 1;i <= n;i++){
			p = point[i];
			
			for(int j = p.head;j;j = nxt[j].next){
				q = point[cToP[nxt[j].x]];
				/*已经在并查集中*/
				if(find(p.code) == find(q.code))
					continue;
				ans += point[cToP[find(q.code)]].w - p.w;
				fa[find(q.code)] = p.code;
			}
		}
		
		for(int i = 1;i <= n;i++){
			ans += fa[i] == i ? point[cToP[i]].w : 0;
		}
		
		cout << ans << endl;
//		printf("%d\n",ans);
	}
}



你可能感兴趣的:(题解,算法,数据结构)