結果

問題 No.1865 Make Cycle
ユーザー kept1994kept1994
提出日時 2022-05-03 23:55:10
言語 PyPy3
(7.3.15)
結果
AC  
実行時間 992 ms / 3,000 ms
コード長 4,454 bytes
コンパイル時間 724 ms
コンパイル使用メモリ 86,756 KB
実行使用メモリ 208,600 KB
最終ジャッジ日時 2023-09-15 21:12:24
合計ジャッジ時間 16,506 ms
ジャッジサーバーID
(参考情報)
judge15 / judge12
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 574 ms
136,280 KB
testcase_01 AC 430 ms
127,996 KB
testcase_02 AC 665 ms
151,400 KB
testcase_03 AC 354 ms
141,932 KB
testcase_04 AC 512 ms
162,040 KB
testcase_05 AC 614 ms
160,672 KB
testcase_06 AC 597 ms
150,936 KB
testcase_07 AC 595 ms
150,464 KB
testcase_08 AC 722 ms
166,356 KB
testcase_09 AC 591 ms
161,340 KB
testcase_10 AC 634 ms
159,344 KB
testcase_11 AC 654 ms
158,736 KB
testcase_12 AC 532 ms
140,064 KB
testcase_13 AC 581 ms
141,640 KB
testcase_14 AC 444 ms
129,412 KB
testcase_15 AC 696 ms
163,460 KB
testcase_16 AC 737 ms
171,432 KB
testcase_17 AC 542 ms
140,276 KB
testcase_18 AC 613 ms
166,280 KB
testcase_19 AC 992 ms
208,600 KB
testcase_20 AC 95 ms
71,184 KB
testcase_21 AC 94 ms
71,232 KB
testcase_22 AC 96 ms
71,428 KB
testcase_23 AC 92 ms
71,388 KB
権限があれば一括ダウンロードができます

ソースコード

diff #

#!/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()
0