解题思路:模拟
本题中,被判定为作弊有两种方式:即(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

京公网安备 11010502036488号