結果

問題 No.1865 Make Cycle
ユーザー kept1994kept1994
提出日時 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
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 567 ms
142,356 KB
testcase_01 AC 385 ms
128,140 KB
testcase_02 AC 590 ms
153,128 KB
testcase_03 AC 308 ms
143,752 KB
testcase_04 AC 475 ms
161,264 KB
testcase_05 AC 578 ms
169,948 KB
testcase_06 AC 510 ms
140,172 KB
testcase_07 AC 538 ms
149,112 KB
testcase_08 AC 695 ms
168,664 KB
testcase_09 AC 565 ms
163,060 KB
testcase_10 AC 598 ms
161,176 KB
testcase_11 AC 585 ms
161,544 KB
testcase_12 AC 472 ms
143,060 KB
testcase_13 AC 524 ms
142,296 KB
testcase_14 AC 441 ms
134,964 KB
testcase_15 AC 700 ms
159,780 KB
testcase_16 AC 702 ms
163,648 KB
testcase_17 AC 523 ms
142,588 KB
testcase_18 AC 593 ms
172,424 KB
testcase_19 AC 966 ms
203,820 KB
testcase_20 AC 47 ms
54,528 KB
testcase_21 AC 48 ms
54,016 KB
testcase_22 AC 47 ms
54,272 KB
testcase_23 AC 47 ms
54,400 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