题目链接:情报站
题意
给你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