This commit is contained in:
alexey_makov 2026-07-01 19:46:51 +03:00
commit 851751ea87
82 changed files with 2093 additions and 0 deletions

171
1G.py Normal file
View file

@ -0,0 +1,171 @@
import sys
# Читаем весь ввод разом
data = sys.stdin.read().split()
if not data:
sys.exit(0)
n = int(data[0])
m = int(data[1])
g = [[] for _ in range(n + 1)]
idx = 2
for _ in range(m):
u = int(data[idx])
v = int(data[idx + 1])
g[u].append(v)
g[v].append(u)
idx += 2
# Структуры для DFS-дерева
tree_adj = [[] for _ in range(n + 1)]
sz = [1] * (n + 1)
parent = [0] * (n + 1)
visited = [False] * (n + 1)
# Строим DFS-дерево итеративно, чтобы не словить RecursionError
stack = [1]
visited[1] = True
order = []
edge_idx = [0] * (n + 1)
while stack:
u = stack[-1]
found = False
while edge_idx[u] < len(g[u]):
v = g[u][edge_idx[u]]
edge_idx[u] += 1
if not visited[v]:
visited[v] = True
parent[v] = u
tree_adj[u].append(v)
stack.append(v)
found = True
break
if not found:
order.append(u)
stack.pop()
# Считаем размеры поддеревьев (снизу вверх)
for u in order:
if parent[u] != 0:
sz[parent[u]] += sz[u]
# Поиск единственного верного пути сверху вниз
u = 1
dp = 1
path = []
target_found = -1
while u != 0:
path.append(u)
rem = n - len(path)
if rem == 0:
break
children = tree_adj[u]
# Проверяем, можем ли мы остановиться прямо сейчас
if rem % 2 == 0 and rem > 0:
target = rem // 2
temp_dp = dp
mask = (1 << (target + 1)) - 1
temp_dp &= mask
# Мысленно добавляем всех детей текущей вершины в рюкзак
for c in children:
temp_dp = (temp_dp | (temp_dp << sz[c])) & mask
if temp_dp & (1 << target):
target_found = target
break
if not children:
break
# Если мы не можем остановиться, мы обязаны шагнуть в самое тяжелое поддерево,
# чтобы "раздробить" его на части.
heavy_child = max(children, key=lambda x: sz[x])
# Все остальные дети навсегда "отваливаются" от пути, добавляем их в рюкзак
mask_dp = (1 << ((n + 2) // 2)) - 1
dp &= mask_dp
for c in children:
if c != heavy_child:
dp = (dp | (dp << sz[c])) & mask_dp
u = heavy_child
if target_found != -1:
# Мы нашли путь! Теперь соберем все отвалившиеся поддеревья, из которых мы набрали target
items = []
for i in range(len(path) - 1):
curr_node = path[i]
next_node = path[i+1]
for c in tree_adj[curr_node]:
if c != next_node:
items.append((c, sz[c]))
last_node = path[-1]
for c in tree_adj[last_node]:
items.append((c, sz[c]))
# Честный алгоритм рюкзака с восстановлением ответа (только для собранных компонент)
track = [-1] * (target_found + 1)
dp_track = 1
for i, (c, w) in enumerate(items):
nxt = (dp_track << w) & ((1 << (target_found + 1)) - 1)
diff = nxt & ~dp_track
if diff:
# Быстрый битовый трюк для итерации только по новым битам
bit = (diff & -diff)
while diff > 0:
pos = bit.bit_length() - 1
if pos <= target_found and track[pos] == -1:
track[pos] = i
diff ^= bit
bit = (diff & -diff)
dp_track |= nxt
if dp_track & (1 << target_found):
break
used_items = set()
curr_w = target_found
while curr_w > 0:
idx = track[curr_w]
used_items.add(idx)
curr_w -= items[idx][1]
a_ans = []
b_ans = []
# Раскидываем полные поддеревья по комнатам А и Б
for i, (c, w) in enumerate(items):
comp_nodes = []
q = [c]
h = 0
while h < len(q):
curr_node = q[h]
h += 1
comp_nodes.append(curr_node)
for child in tree_adj[curr_node]:
q.append(child)
if i in used_items:
a_ans.extend(comp_nodes)
else:
b_ans.extend(comp_nodes)
# Вывод
print(len(path), len(a_ans))
print(*path)
print(*a_ans)
print(*b_ans)
else:
# Железобетонный фолбек (теоретически недостижим на валидных данных)
half = (n - 1) // 2
print(1, half)
print(1)
print(*range(2, 2 + half))
print(*range(2 + half, n + 1))