結果
| 問題 |
No.1865 Make Cycle
|
| コンテスト | |
| ユーザー |
|
| 提出日時 | 2022-05-03 23:55:10 |
| 言語 | PyPy3 (7.3.15) |
| 結果 |
AC
|
| 実行時間 | 966 ms / 3,000 ms |
| コード長 | 4,454 bytes |
| コンパイル時間 | 202 ms |
| コンパイル使用メモリ | 81,920 KB |
| 実行使用メモリ | 203,820 KB |
| 最終ジャッジ日時 | 2024-07-02 22:40:23 |
| 合計ジャッジ時間 | 14,289 ms |
|
ジャッジサーバーID (参考情報) |
judge5 / judge2 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 4 |
| other | AC * 20 |
ソースコード
#!/usr/bin/env python3
import sys
MOD = 998244353
from collections import deque
import heapq
class TopologicalTree:
def __init__(self, N) -> None:
self.topologicalOrder = []
self.N = N
self.seen = [0] * N
self.G = [[] for _ in range(N)]
self.degree = [0] * N # 各ノードの入次数
return
# 辺の追加
def addEdge(self, fromNode: int, toNode: int):
self.G[fromNode].append(toNode)
self.degree[toNode] += 1
def topologicalSort(self):
deq = deque([node for node in range(self.N) if self.degree[node] == 0]) # 入次数0のものがスタート
# 片っ端から入次数0のものを取り出していく。取り出すとそのノードから遷移するノードの入次数をデクリメントする。
while deq:
node = deq.popleft()
self.topologicalOrder.append(node)
for t in self.G[node]:
self.degree[t] -= 1
if self.degree[t] == 0:
deq.append(t)
if [i for i in range(self.N) if self.degree[i]]: # 最終的な入次数が0じゃないものが残る場合循環がある。
return None
return self.topologicalOrder
def topologicalSort_heapq(self):
"""
辞書順最小のトポロジカル順序を必要とする場合。
queueではなくheapqを使うため、入次数0のものの中で辞書順最小のnodeが選ばれる。
"""
hq = [node for node in range(self.N) if self.degree[node] == 0] # 入次数0のものがスタート
heapq.heapify(hq)
# 片っ端から入次数0のものを取り出していく。取り出すとそのノードから遷移するノードの入次数をデクリメントする。
while hq:
node = heapq.heappop(hq)
self.topologicalOrder.append(node)
for t in self.G[node]:
self.degree[t] -= 1
if self.degree[t] == 0:
heapq.heappush(hq, t)
if [i for i in range(self.N) if self.degree[i]]: # 最終的な入次数が0じゃないものが残る場合循環がある。
return None
return self.topologicalOrder
def isUniqueOrder(self):
"""
トポロジカル順序が一意に定まるかを返す。
一般に、トポロジカルソート順 v1,v2,…,vN において、連続する2要素 vi,vi+1 の間に辺がないとき、その2要素を入れ替えることができることを利用。
https://drken1215.hatenablog.com/entry/2021/01/02/164800
"""
for node in range(self.N - 1):
if not self.topologicalOrder[node + 1] in self.G[self.topologicalOrder[node]]:
return True
else:
return False
# トポロジカルソートした結果の数列が何通りあるか -> bitDP
# https://ferin-tech.hatenablog.com/entry/2017/01/24/184750
def main():
N, Q = list(map(int, input().split()))
queries = []
for _ in range(Q):
queries.append(list(map(int, input().split())))
# True ------ ok | ng ---- False
def is_ok(k: int):
tr = TopologicalTree(N)
for a, b in queries[:k]:
tr.addEdge(a - 1, b - 1)
l = tr.topologicalSort()
# print("#", l)
return l != None # 条件式
def binSearch(ok: int, ng: int):
# print(ok, ng) # はじめの2値の状態
while abs(ok - ng) > 1: # 終了条件(差が1となり境界を見つけた時)
mid = (ok + ng) // 2
# print("target > ", mid)
result = is_ok(mid)
# print(result)
if result:
ok = mid # midが条件を満たすならmidまではokなのでokの方を真ん中まで持っていく
else:
ng = mid # midが条件を満たさないならmidまではngなのでngの方を真ん中まで持っていく
# print(ok, ng) # 半分に切り分ける毎の2値の状態
return ng # 関数呼び出し時の引数のngは絶対評価されないのでngに書く値が答えになりうるならその数マイナス1を指定する。
ans = binSearch(0, Q)
print(ans if ans < Q else -1)
return
if __name__ == '__main__':
main()