2020 智算之道 情报站

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述记录一下很普通很慢的解法(还不是因为自己菜)
在这里插入图片描述

思路:

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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值