uoj 175. 新年的网警

8 篇文章 0 订阅

题意:

在这新年的第一天,猴族首领猴腮雷打算来整治一下网络风气。这时,他听说在一个叫做 Universal OJ 用户群 的 QQ 群中有人在散播(开)谣言(车),于是他就派了一群网警把这个用户群里的人都抓了回来,试图找到谣言的源头。
这个用户群中有 nn 个人,这些人中存在 mm 对双向的直接认识关系,这个社交网络中任意两个人都是直接或者间接认识的。经过研究,谣言的散播以如下的方式进行:
首先在某个时刻 TT,谣言的源头想出了一个谣言,于是他在时刻 T+1T+1 把这个谣言讲给了所有和他直接认识的人听。
如果一个人在第 ii 个时刻第一次听到了这个谣言,他会在第 i+1i+1 时刻时把这个谣言讲给所有和他直接认识的人听。
现在网警们问出来每一个人第一次听到这个谣言的时间,但是遗憾的是他们并不知道 TT 的具体数值。而且,谣言的发起者不会坐以待毙,他可以随便回答一个时间(当然也可以回答真实时间),而其他不是谣言的源头的人一定不会撒谎。(注意:网警知道谣言的发起者可以说谎)
猴族首领猴腮雷根据网警们递交上来的口供,非常轻易的就推理出了谣言的源头是谁并把他绳之以法。但是他发现,有些情况下,根据口供还不能唯一确定嫌疑人(即嫌疑人可能有多个),于是他想要知道哪些人是“安全的谣言发起人”。
一个人是安全的谣言发起人,当且仅当他可以通过捏造口供使得猴腮雷无法唯一确定嫌疑人(具体可以看样例解释)。

题解:

%%%羊老师教我做题。
等于是问是否有另一个点到各点距离和当前点一样。
显然,这个点的距离不能超过2。然后容易想到这两个点的连边情况要完全一样(忽略他们相互连边)。
然后就完了吗?偷看题解,发现还有两种情况:
1、度数为1的点。
2、连向度数为1的点的点。
容易证明
然后hash判边是否相同。
code:

#include<map>
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#define LL unsigned long long
using namespace std;
const LL base=10037;
map <LL,int> mp;
LL pre[100010],hash[100010];
int n,m,d[100010],ans[100010];
struct node{
    int y,next;
}a[400010];int len,last[100010];
inline void write(int x)
{
    if (!x) return (void)puts("0");
    if (x < 0) putchar('-'), x = -x;
    static short s[12], t;
    while (x) s[++t] = x % 10, x /= 10;
    while (t) putchar('0' + s[t--]);
}
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void ins(int x,int y)
{
    a[++len].y=y;
    a[len].next=last[x];last[x]=len;
}
int main()
{
    int T;T=read();
    pre[0]=1;for(int i=1;i<=100000;i++) pre[i]=pre[i-1]*base;
    while(T--)
    {
        n=read();m=read();
        for(int i=1;i<=n;i++) hash[i]=0;
        len=0;memset(last,0,sizeof(last));
        memset(d,0,sizeof(d));mp.clear();
        for(int i=1;i<=m;i++)
        {
            int x,y;x=read();y=read();
            d[x]++;d[y]++;hash[x]+=pre[y];hash[y]+=pre[x];
            ins(x,y);ins(y,x);
        }
        memset(ans,0,sizeof(ans));
        for(int i=1;i<=n;i++) mp[hash[i]]++;
        int k=0;
        for(int x=1;x<=n;x++)
        {
            if(d[x]==1||mp[hash[x]]>1) k++,ans[x]=1;
            else
            {
                bool flag=false;
                for(int i=last[x];i;i=a[i].next)
                {
                    int y=a[i].y;
                    if(d[y]==1) {flag=true;break;}
                }
                if(flag) k++,ans[x]=1;
            }
        }
        mp.clear();
        for(int i=1;i<=n;i++) hash[i]+=pre[i],mp[hash[i]]++;
        for(int i=1;i<=n;i++)
            if(ans[i]==0&&mp[hash[i]]>1) k++,ans[i]=1;
        write(k);putchar('\n');
        for(int i=1;i<=n;i++)
            if(ans[i]) write(i),putchar(' ');
        putchar('\n');
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值