题解 | #火眼金睛#
火眼金睛
https://www.nowcoder.com/practice/d311403b15b8495b81b622edaefd5b5a
解题思路:模拟
本题中,被判定为作弊有两种方式:即(a)互相回答问题判定;(b)提问被2个以上作弊者回答。整体思路是:先找到所有(a)类作弊者,再一步步找出(b)类作弊者。
使用user_to_user,users,questions三个字典,以及集合ans,它们的用途如下:
- user_to_user:记录每个用户 id 回答过谁的问题。用于快速找出(a)类作弊者
- questions:记录每个问题的提问者、以及回答者中已经被标记为作弊的用户数量。当被标记次数加至2时,提问者判定为(b)类作弊者。
- users:记录每个id回答过问题的序号。用于发现新的作弊者后,快速定位并标记其回答过的问题。
- ans:记录已经被标记为作弊者的id。
模拟的过程如下:
1. 对于输入的每个问题,找出提问者 q 和若干个回答者。然后:
1.1. 对于每个回答者 x,将问题的序号 i 放入 users[x] 中
1.2. 如果 q 已经回答过 x 的问题,他们在第一轮同时判定为作弊。否则将 x 放入 user_to_user[q] 中
1.3. 初始化 questions 中的问题 i :记录下提问者 q ,被标记答者数初始化为 0
2. 第一轮被标记为作弊的 id 已经被记录在 ans 中。可以开始第二轮标记。具体做法是:对于每个作弊者 x 和其回答过的每个问题 q,问题 q 被标记一次。然后如果这是 q 第二次被标记,那么 q 的提问者将在第二轮被判定为作弊。
3. 重复 2 的过程,直到没有新的作弊者被判定为作弊。
from collections import defaultdict def solve(): ans = set() user_to_user = defaultdict(set) # i提问被谁回答过 users = defaultdict(list) # id, questions questions = {} # id: [user, cheater_count] for i in range(int(input())): ipt = [int(x) for x in input().split()] q, a = ipt[0], set(ipt[2:]) for x in a: # i:问题序号,q: 提问者,x:回答者 users[x].append(i) if q in user_to_user[x]: ans |= {q, x} elif q != x: user_to_user[q].add(x) questions[i] = [q, 0] s = ans while s: ss = set() # 下轮即将更新的作弊者 for x in s: for q in users[x]: questions[q][1] += 1 # cheater_count 增加 1 if questions[q][1] == 2: ss.add(questions[q][0]) ans |= ss s = ss print(len(ans)) if ans: print(str(list(ans)).replace(',','')[1:-1]) while 1: try: solve() except EOFError: break