记录一下很普通很慢的解法(还不是因为自己菜)
思路:
1.建立一个标技数组,表示数字 i 是否已知
2.对每个数 x 都建立一个关系队列,用来记录与x有关系的数字y(即只知道 x+y 的和并且 x、y 均未知的情况)这里要格外注意x=y的情况,这种与第一种情报等价,即告诉了x的值
3.每次读取情报 建立一个更新队列,记录需要更新的数字
(1)若是第一种情报,如果x没有被标记(即未知),那么标记x,并把x放入更新队列
(2)若是第二种情报,x、y均未知时,将x放入y对应的关系队列,y放入x对应的关系队列
若其中一个数已标记,另一个数未标记,则未知的这个数就可以求出了,(假设x是未被标记的),那么标记x,并放入更新队列
更新队列里的数意味着:这些数因为这个情报从未知 转变为 已知,那么之前与它相关的那些数(只知道 x+y 的和而放进去的数)就可以根据之前情报给的和确定这些数啦
4.所以第四步就是对更新队列中的数对应的关系队列里的数进行标记(有点绕,但是代码敲出来就懂啦)
注意:这些新标记的数,如果之前没有标记,那么这些数标记后,也要放进更新队列中
因为一旦一个数确定了(假设是x),那么之前只知道与他有关系(知道x+y的和)的所有数(即他的关系队列里的数),就都能确定了
代码
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
queue<int>v[300009];///关系队列
int a[300009]={0};///标技数组
int main()
{
int n,m,t,ans=0,w,e,x,y;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d",&t);
queue<int>p;///更新队列
if(t==2)
{
scanf("%d%d",&w,&e);
if(ans==n){printf("%d\n",ans);continue;}
if(w==e)///等价于告诉了w的值
{
if(!a[w])///之前未被标记,即此数未知
{
ans++;
a[w]=1;
p.push(w);///状态改变,放入更新队列
}
}
if(w!=e)
{
if(!a[e]&&!a[w])///只知道 x+y 的和并且 x、y 均未知的情况
{
v[w].push(e);
v[e].push(w);
}
else if(!a[e])///w已知,e未知
{
ans++;
a[e]=1;///e可以由关系式确定,所以状态改变为已知
p.push(e);///状态改变,放入更新队列
}
else if(!a[w])
{
ans++;
a[w]=1;///w可以由关系式确定,所以状态改变为已知
p.push(w);///状态改变,放入更新队列
}
}
while(!p.empty())
{
x=p.front();///取更新队列里的数
p.pop();
while(!v[x].empty())///x的关系队列
{
y=v[x].front();///关系队列里的数
v[x].pop();
if(!a[y])
{
ans++;
a[y]=1;///x已知,那么y可有关系式确定,状态改变为已知
p.push(y);///状态改变,放入更新队列
}
}
}
}
else///第一种情报,每一步的作用与上面大体相同,不再赘述
{
scanf("%d",&w);
if(ans==n){printf("%d\n",ans);continue;}
if(!a[w])
{
ans++;
a[w]=1;
p.push(w);
}
while(!p.empty())
{
x=p.front();
p.pop();
while(!v[x].empty())
{
y=v[x].front();
v[x].pop();
if(!a[y])
{
ans++;
a[y]=1;
p.push(y);
}
}
}
}
printf("%d\n",ans);
}
return 0;
}