Orig Description
G: DAG Trio / DAG Trio
This problem is the same setting as D: DAG Trio (Easy) except for the constraints.
Prologue
My little brother has been muttering "dag torio" recently.
Worried, I looked it up and found that in his class, $k$-DAG (a directed graph in which removing $k$ edges makes the number of connected components $k$, and all of them are DAG) is popular, and 3-DAG is called DAG trio.
To be respected by my brother, I decided to make a program that determines whether a given graph is DAG trio or not.
Problem Statement
Given a directed graph with $N$ vertices and $M$ edges.
Each vertex is numbered from $1$ to $N$.
Each directed edge is numbered from $1$ to $M$.
Directed edge $i$ goes from vertex $a_i$ to vertex $b_i$.
The graph is connected and simple (there is a path between any two vertices when ignoring the direction of edges, and there are no self-loops or multiple edges).
If the given graph is DAG trio, output "YES", otherwise output "NO".
Input
$N \ M$
$a_1 \ b_1$
$a_2 \ b_2$
$\vdots$
$a_M \ b_M$
Constraints
$3 \le N \le 500$
$\max(3, N−1) \le M \le 30000$
$1 \le a_i, b_i \le N$
The graph is connected when ignoring the direction of edges.
$a_i \neq b_i$ for all $i$.
$\{a_i, b_i\} \neq \{a_j, b_j\}$ for all $i \neq j$.
Output
Output "YES" or "NO" in a single line.
Sample
Sample Input 1
3 3
1 2
2 3
3 1
Sample Output 1
YES
Sample Input 2
6 7
1 2
2 3
4 3
4 5
5 6
6 4
3 6
Sample Output 2
YES
Sample Input 3
7 10
4 2
4 7
4 6
2 7
2 5
2 1
5 6
1 3
6 3
4 3
Sample Output 3
NO
Sample Input 4
4 4
1 2
3 2
4 3
2 4
Sample Output 4
YES
Sample Input 5
8 9
5 1
3 8
1 2
4 8
4 7
7 5
6 5
3 2
4 2
Sample Output 5
YES
Extracted Specification
Canonicalized Input Description:
An integer \(N\) (3 ≤ \(N\) ≤ 500), representing some quantity or size.
An integer \(M\) (max(3, \(N\) - 1) ≤ \(M\) ≤ 30000), representing some quantity or size.
A list of \(M\) tuples \((a_i, b_i)\), where each tuple contains two integers and \(1 ≤ a_i, b_i ≤ N\).
### Example Input:
```
3 3
1 2
2 3
3 1
```
### Function Signature:
Write a function f(N, M, edges) that takes in the input.
```python
def f(N: int, M: int, edges: List[Tuple[int, int]]):
'''
N: an integer
M: an integer
edges: a list of tuples with two integers each
'''
Test Case Generator
Certainly! Below is the implementation of a test case generator for the provided problem statement "DAG Trio".
```python
import random
def generate_graph(N, M):
edges = set()
while len(edges) < M:
a = random.randint(1, N)
b = random.randint(1, N)
if a != b:
edge = (a, b)
reverse_edge = (b, a)
if edge not in edges and reverse_edge not in edges:
edges.add(edge)
return list(edges)
def is_connected(graph, N):
from collections import defaultdict, deque
adjacency_list = defaultdict(list)
for a, b in graph:
adjacency_list[a].append(b)
adjacency_list[b].append(a)
visited = set()
queue = deque([1])
visited.add(1)
while queue:
node = queue.popleft()
for neighbor in adjacency_list[node]:
if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)
return len(visited) == N
def tcgen():
N = random.randint(3, 500)
M = random.randint(max(3, N - 1), 30000)
while True:
graph = generate_graph(N, M)
if is_connected(graph, N):
break
return N, M, graph
# Generate a test case
N, M, graph = tcgen()
print(N, M)
for a, b in graph:
print(a, b)
```
### Explanation:
1. **generate_graph(N, M)**: Generates a random graph with N vertices and M edges. It ensures that there are no self-loops and no multiple edges between any two vertices.
2. **is_connected(graph, N)**: Checks if the generated graph is connected by performing BFS (Breadth-First Search).
3. **tcgen()**: Generates the number of vertices \(N\) and edges \(M\) within the given constraints, then repeatedly generates and checks random graphs until a connected one is found.
4. **print(N, M)**: Prints the number of vertices and edges.
5. **for a, b in graph**: Prints each edge of the graph.
This generator ensures that the generated graph adheres to the constraints and is connected, as required by the problem statement.
Extract Arguments
def extract_arguments(fh):
N, M = map(int, fh.readline().strip().split())
edges = [tuple(map(int, fh.readline().strip().split())) for _ in range(M)]
return N, M, edges
# if __name__ == "__main__":
# input_path = sys.argv[1]
# with open(input_path, 'r') as fh:
# N, M, edges = extract_arguments(fh)
# f(N, M, edges)