智算之道 T3.情报站(并查集)

题目链接:情报站

题意

给你n个数, 获取m条情报,情报分为两种:
1.知道了数列中第 x 个数的值。
2.知道了数列中第 x 个数和第 y 个数的和。
每得知一条情报,输出得知了前 i 条情报之后数列中已经能够确定的数的数量

题解

一种解法为并查集。
情报一:如果这个数的终极父结点是未被访问的,那么将该数的终极父结点标记,并将这个终极父结点的权值加在答案上。(权值代表该终极父结点所在连通分支里的结点总数。)
情报二:你会遇到四种情况,这两个数你都知道、你只知道其中一个(2)以及都不知道这四种情况。如果你都知道就没有讨论的必要,讨论的都是不知道或者只知道其中一个的情况。

1.当你知道两个数其中一个时,另一个数就可得知,将未知数的终极父结点标记,答案加上这个终极父结点的权值。
2.当你两个数都不知道时(a,b),我们就可以将a的终极父结点作为b的终极父结点的父结点形成一个连通分支,并且将新的连通分支的终极父结点(a的终极父结点)的权值加上b的终极父结点的权值。

由于只是对终极父结点进行操作,所以同一个连通分支只会出现两种情况,要么里面的元素全部知道,要么全部不知道,不会出现一部分知道一部分不知道的情况。

在情报二下,同一个连通分支的元素只会遇到都不知道的讨论情况,而对于已经在同一个连通分支的未知元素,重复合并会导致答案重复添加,所以在情报二的情况下,我们只讨论在不同连通分支的元素。

代码

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<bitset>
#include<cassert>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<deque>
#include<iomanip>
#include<list>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
using namespace std;
//extern "C"{void *__dso_handle=0;}
typedef long long ll;
const int N=3e5+10;

int f[N],cnt[N],vis[N];
int find(int x)
{
	return x==f[x]?x:f[x]=find(f[x]);
}
void init(int n)
{
	for(int i=1;i<=n;i++) f[i]=i,cnt[i]=1;
}
int main()
{
	int n,m,ans=0;
	scanf("%d%d",&n,&m);
	init(n);
	while(m--)
	{
		int op,x,y;
		scanf("%d%d",&op,&x);
		if(op==1)
		{
			int fx=find(x);
			if(!vis[fx])
			{
				vis[fx]=1;
				ans+=cnt[fx];
			}
		}
		else
		{
			scanf("%d",&y);
			int fx=find(x),fy=find(y);
			if(fx!=fy) //不同连通分支
			{
				if(!vis[fx] && vis[fy])
				{
					vis[fx]=1;
					ans+=cnt[fx];
				}
				else if(vis[fx] && !vis[fy])
				{
					vis[fy]=1;
					ans+=cnt[fy];
				}
				else if(!vis[fx] && !vis[fy])
				{
					f[fx]=fy;
					cnt[fy]+=cnt[fx];
				}
			}
		}
		printf("%d\n",ans);
	}
}
//5 5
//2 1 3
//2 1 4
//2 2 3
//2 2 4
//1 1

猜你喜欢

转载自blog.csdn.net/weixin_44235989/article/details/107473554