Here is the link to the contest. All problems are from Codeforces' problem set.
To determine if each access key is secure, we need to validate it against three specific rules.
- Digits appear before letters:
All digits (if present) must appear before any letters. This means the transition from digits to letters can happen only once, and once it does, no digit should appear afterward.
- For example,
"123abc"is valid, but"a1b2"is not.
- Digits are non-decreasing:
The digits must be in non-decreasing order. As we read through the digits from left to right, each digit must be greater than or equal to the previous one.
- For example,
"1125"is valid, but"132"is not, because3comes before2, which violates the order.
- Letters are non-decreasing:
The letters must also be in non-decreasing alphabetical order or remain the same.
- For example,
"abc"and"aabbcc"are valid, but"acb"is not.
Implementation Approach:
- Iterate over the string character by character.
- Maintain two variables:
- One to track the last digit seen.
- One to track the last letter seen.
- If the current character is a digit:
- Check that it is not less than the last digit.
- If we have already seen a letter and then encounter a digit afterward, mark the key invalid immediately.
- If the current character is a letter:
- Check that it is not less than the last letter.
By applying these checks for each test case, we can determine whether a given access key is secure. If all conditions are satisfied, output "YES"; otherwise, output "NO". This approach is efficient and works well within the given constraints.
t = int(input())
for _ in range(t):
n = int(input())
key = input()
last_digit = '0'
last_letter = 'a'
seen_letter = False
is_secure = True
for ch in key:
if ch.isdigit():
if seen_letter:
is_secure = False
break
if ch < last_digit:
is_secure = False
break
last_digit = ch
else:
if ch < last_letter:
is_secure = False
break
last_letter = ch
seen_letter = True
print("YES" if is_secure else "NO")
Jean wants to make as many hikes as possible. Each hike requires exactly $$$k$$$ consecutive days of good weather, meaning the weather values must be all zeros during those $$$k$$$ days.
Suppose we identify a continuous block of days where the weather is good (all zeros), and the block length is $$$len$$$. This means Jean has $$$len$$$ consecutive days suitable for hiking.
Each hike takes $$$k$$$ days, and after each hike, Jean must take a rest day before starting another hike. This means every hike plus its mandatory rest day consumes $$$k + 1$$$ days, except for the last hike in the block, which does not require a rest day afterward.
To handle this last hike exception neatly, we imagine adding an imaginary rest day at the end of the block, making the effective block length $$$len + 1$$$.
With this imaginary day included, the entire block can be divided into chunks of length $$$k + 1$$$, where each chunk represents one hike plus its rest day.
Therefore, the maximum number of hikes that fit in the block is calculated by:
where the keyword floor means rounding down to the nearest integer.
If the weather array contains multiple such blocks separated by rainy days (days with 1), we apply this formula to each block independently.
Finally, the sum of hikes from all blocks will be the maximum number of hikes Jean can make.
This approach efficiently accounts for both the length of consecutive good weather days and the mandatory rest days, allowing us to quickly compute the maximum hikes possible in the entire period.
t = int(input())
for _ in range(t):
n, k = map(int, input().split())
a = list(map(int, input().split()))
hikes = 0
length = 0
for day in a:
if day == 0:
length += 1
else:
hikes += (length + 1) // (k + 1)
length = 0
hikes += (length + 1) // (k + 1)
print(hikes)
To solve the problem efficiently, we need to understand the true cost of using a teleporter at position $$$i$$$. In order to use it, we must first walk from point 0 to point $$$i$$$, which costs $$$i$$$ coins since each step costs 1 coin. Then, we must pay an additional $$$a_i$$$ coins to activate the teleporter. Therefore, the total cost to use the teleporter at position $$$i$$$ is
Once we use a teleporter, we are immediately teleported back to point 0, and that specific teleporter becomes unusable.
This reset behavior means that each teleportation brings us back to the same starting point, making the decision to use a teleporter independent of previous choices. In other words, no matter what teleporters we used before, we are always at point 0 again with some coins remaining and a new subset of teleporters to choose from. This observation is key because it allows us to analyze and handle each teleportation individually, based solely on how much it costs.
With this in mind, the optimal strategy is to compute the total cost $$$i + a_i$$$ for each teleporter, then sort all teleporters by their total cost in ascending order. After sorting, we greedily select teleporters starting from the cheapest one, and for each, we subtract its cost from our total coins $$$c$$$. We continue this process until we can no longer afford the next cheapest teleporter. This greedy method guarantees the maximum number of teleporters we can use, as there is no benefit to delaying a cheaper one in favor of a more expensive option later.
In terms of time complexity, this approach is efficient and well within the problem's constraints. Calculating total costs takes $$$O(n)$$$, sorting the teleporters requires $$$O(n \log n)$$$, and the greedy selection phase runs in $$$O(n)$$$. Therefore, the overall complexity per test case is
Since the sum of $$$n$$$ over all test cases is constrained to $$$2 \times 10^5$$$, this solution performs well for all valid inputs.
t = int(input())
for _ in range(t):
n, coin = map(int, input().split())
costs = list(map(int, input().split()))
total_costs = [i + costs[i - 1] for i in range(1, n + 1)]
total_costs.sort()
count = 0
for cost in total_costs:
if cost <= coin:
coin -= cost
count += 1
else:
break
print(count)
Run DFS on $$$G$$$ to label components.
Cut edges in $$$F$$$ crossing $$$G$$$ groups, then count $$$F$$$ components via DFS.
To solve the problem, start by understanding the concept of connected components in a graph. A connected component is a set of vertices where each vertex can be reached from any other vertex in the same set by traversing edges.
- First, consider the original graph $$$G$$$.
- Using DFS (Depth-First Search) on $$$G$$$, assign a component index to each vertex.
- Vertices in the same component share the same index, while vertices in different components have different indices.
Next, examine the graph $$$F$$$ and its edges. For each edge in $$$F$$$:
- Check the component indices of the two vertices it connects according to $$$G$$$.
- If these vertices belong to different components in $$$G$$$ (i.e., their component indices differ), then this edge does not respect the connectivity of $$$G$$$ and should be removed from $$$F$$$.
- Each such removal counts as one operation.
- This step ensures that $$$F$$$ does not create connections that are not present in $$$G$$$.
After removing these invalid edges:
- Run DFS again on the updated graph $$$F$$$ to find its connected components.
- Count how many connected components $$$F$$$ has now.
- Since $$$F$$$ started as a subgraph of $$$G$$$ but had edges removed, it might have more connected components than $$$G$$$.
- To preserve $$$G$$$’s connectivity, the number of connected components in $$$F$$$ should be equal to that in $$$G$$$.
- If $$$F$$$ has more connected components, you need to perform operations to fix the connectivity — essentially, connecting these components back together.
The number of operations needed is:
Thus, the total operations to transform $$$F$$$ to have the same connectivity as $$$G$$$ is:
- The sum of edges removed plus
- The number of connectivity fixes described above.
Summary:
- Use DFS to find connected components in both graphs.
- Remove edges in $$$F$$$ that violate $G$’s connectivity.
- Calculate how many connectivity fixes are needed to make $$$F$$$ and $$$G$$$ equivalent in terms of connected vertices.
- This DFS-based approach efficiently solves the problem without requiring more advanced data structures like DSU.
from collections import defaultdict
def dfs(node, adj, visited, component_id, comp_id):
stack = [node]
while stack:
curr = stack.pop()
if visited[curr]:
continue
visited[curr] = True
component_id[curr] = comp_id
for neighbor in adj[curr]:
if not visited[neighbor]:
stack.append(neighbor)
t = int(input())
for _ in range(t):
n, m1, m2 = map(int, input().split())
F_adj = [[] for _ in range(n + 1)]
G_adj = [[] for _ in range(n + 1)]
F_edges = []
for _ in range(m1):
u, v = map(int, input().split())
F_adj[u].append(v)
F_adj[v].append(u)
F_edges.append((u, v))
for _ in range(m2):
u, v = map(int, input().split())
G_adj[u].append(v)
G_adj[v].append(u)
# Build G components
g_component = [0] * (n + 1)
visited = [False] * (n + 1)
g_id = 1
for i in range(1, n + 1):
if not visited[i]:
dfs(i, G_adj, visited, g_component, g_id)
g_id += 1
# rmove edges in F that violate G connectivi
remove_count = 0
new_F_adj = [[] for _ in range(n + 1)]
for u, v in F_edges:
if g_component[u] != g_component[v]:
remove_count += 1
else:
new_F_adj[u].append(v)
new_F_adj[v].append(u)
# build F components on filtered F graph
f_component = [0] * (n + 1)
visited = [False] * (n + 1)
f_id = 1
for i in range(1, n + 1):
if not visited[i]:
dfs(i, new_F_adj, visited, f_component, f_id)
f_id += 1
g_to_f = defaultdict(set)
for i in range(1, n + 1):
g_to_f[g_component[i]].add(f_component[i])
add_count = sum(len(s) - 1 for s in g_to_f.values())
print(remove_count + add_count)
There is a powerful technique called Disjoint Set Union (DSU) or Union-Find that can solve this problem more efficiently. DSU helps quickly find and merge connected components without needing repeated DFS traversals. Although you haven't learned DSU yet, you will study it in the future, and mastering it will make solving connectivity problems like this much easier and faster..
Look at pairs of names and notice where they first differ.
Think about how letter orders can be arranged to satisfy these differences.
Let’s start by understanding what it means when we say one string $$$S$$$ is less than another string $$$T$$$ in lexicographical order. Suppose $$$S = \text{abcxyz}$$$ and $$$T = \text{abcuv}$$$. According to the definition of lex order, $$$S \lt T$$$ if and only if the first position where they differ determines the order. In this example, the first differing characters are $$$x$$$ in $$$S$$$ and $$$u$$$ in $$$T$$$. So, $$$S \lt T$$$ holds if and only if $$$x \lt u$$$ in the alphabet order we are trying to find.
From this observation, we can transform the problem of comparing whole strings into constraints about the order of individual letters. For every pair of consecutive names $$$\text{name}_1 \lt \text{name}_2$$$, $$$\text{name}_2 \lt \text{name}_3$$$, and so on, we extract the relationship between letters where they first differ. These relationships give us inequalities like $$$x \lt u$$$.
The core question then becomes: Is there a permutation of the alphabet that satisfies all these letter-order conditions? This is a classic problem of finding a topological order in a directed graph. Each letter represents a node, and the inequalities correspond to directed edges between nodes. If a valid topological order exists, it represents a letter ordering that satisfies all constraints.
However, there is a tricky case to consider. Suppose we have a condition like $$$xy \lt x$$$, meaning a longer string starting with $$$xy$$$ is less than its prefix $$$x$$$. According to lexicographical rules, this cannot happen since a prefix is always less than a longer string that starts with it. If such a case appears, then no valid letter ordering exists, and the answer is no solution.
This careful check is essential because such cases might not appear in pretests but can occur in some inputs, so it’s important to handle them explicitly.
Time Complexity:
Let $$$n$$$ be the number of names and $$$L$$$ be the maximum length of a name. Comparing consecutive names takes $$$O(L)$$$ time per pair, so extracting all constraints requires $$$O(n \times L)$$$. Building the graph for letters involves at most 26 nodes (for each Latin letter), so topological sorting runs in $$$O(26 + E)$$$, where $$$E$$$ is the number of edges (constraints), which is at most $$$O(n)$$$. Overall, the solution runs efficiently within $$$O(n \times L)$$$ time, suitable for the given problem constraints.
from collections import deque
n = int(input())
words = [input().strip() for _ in range(n)]
def helper():
graph = [[] for _ in range(26)]
in_degree = [0 for _ in range(26)]
for i in range(n - 1):
word1 = words[i]
word2 = words[i + 1]
pointer = 0
while pointer < len(word1) and pointer < len(word2):
if word1[pointer] != word2[pointer]:
index1 = ord(word1[pointer]) - ord("a")
index2 = ord(word2[pointer]) - ord("a")
graph[index1].append(index2)
in_degree[index2] += 1
break
pointer += 1
if pointer == len(word2) and len(word1) > len(word2):
return "Impossible"
queue = deque()
for i in range(26):
if in_degree[i] == 0:
queue.append(i)
order = []
while queue:
curr = queue.popleft()
order.append(curr)
for neighbor in graph[curr]:
in_degree[neighbor] -= 1
if in_degree[neighbor] == 0:
queue.append(neighbor)
if len(order) < 26:
return "Impossible"
return ''.join(chr(i + ord('a')) for i in order)
print(helper())
Explore the graph using DFS and pay attention to the edges that point back to ancestors.
Separate edges into two groups based on their role in DFS to avoid cycles within a color.
The problem involves coloring the edges of a directed graph to avoid cycles formed by edges of the same color. One effective approach is to run a DFS (Depth-First Search) on the graph and classify edges into two types based on the DFS traversal:
Back edges: These are edges $$$(u,v)$$$ where $$$v$$$ is an ancestor of $$$u$$$ in the DFS tree. In other words, there is a path from $$$v$$$ to $$$u$$$ using tree edges, and $$$(u,v)$$$ points back up the DFS tree.
Non-back edges (white edges): These include tree edges, forward edges, and cross edges, which do not point back to ancestors in the DFS tree.
The coloring strategy is to color all back edges with one color (say, black) and all other edges with another color (say, white). This is guaranteed to produce a good 2-coloring, meaning that there will be no monochromatic cycles.
Why does this work?
It can be proven that any cycle in the graph must contain at least one back edge and at least one non-back (white) edge. This is because cycles “wrap around” in the graph, and the presence of back edges reflects these backward connections. Each back edge directly participates in forming at least one cycle by connecting a descendant node back to an ancestor.
To understand this further, consider how vertices can be renumbered based on DFS traversal. We assign an ID to each vertex such that the ID of a parent vertex $$$id(p)$$$ is greater than the IDs of all its children $$$id(c)$$$. This is done by processing children first and then assigning the minimal free number to the parent after processing all its descendants.
With this numbering, we can observe the following:
- For white edges $$$(u,v)$$$ (including tree, forward, and cross edges), the condition $$$id(u) \gt id(v)$$$ always holds. This is because:
- Forward edges go from ancestors to descendants, so $$$id(u) \gt id(v)$$$.
- Cross edges go between already visited nodes where $$$v$$$ was processed before $$$u$$$, so $$$id(v) \lt id(u)$$$.
For back edges $$$(u,v)$$$, the opposite condition holds: $$$id(u) \lt id(v)$$$, since the edge points from a descendant back to an ancestor.
Since any cycle contains edges where $$$id(u) \gt id(v)$$$ and edges where $$$id(u) \lt id(v)$$$, it must contain both white and black edges. This ensures that no cycle can be formed entirely from edges of the same color, which is exactly what a good coloring requires.
In summary, by using DFS to separate edges into back and non-back edges and coloring them differently, we guarantee a minimal 2-coloring where no monochromatic cycles exist. This approach efficiently solves the problem of minimum good coloring.
n, m = map(int, input().split())
graph = [[] for _ in range(n)]
edges = []
for i in range(m):
u, v = map(int, input().split())
u -= 1
v -= 1
graph[u].append((v, i))
edges.append((u, v))
color = [0] * m
visited = [0] * n
need_two_colors = False
def dfs(u):
global need_two_colors
visited[u] = 1
for v, idx in graph[u]:
if visited[v] == 0:
color[idx] = 1
dfs(v)
elif visited[v] == 1:
color[idx] = 2
need_two_colors = True
else:
color[idx] = 1
visited[u] = 2
for i in range(n):
if visited[i] == 0:
dfs(i)
if need_two_colors:
print(2)
else:
print(1)
print(*color)



