Поток переполняет int — где именно копится переполнение и почему недостаточно long long только на ответе?
В задаче пропускные способности до $10^9$, рёбер до $10^5$. Сделал тип ответа long long, но всё равно ловлю переполнение / неправильный поток. Подозреваю, что int где-то внутри. Где именно в потоковом коде переполнение опасно и что менять на long long?
2 ответа
Замены типа только у итогового flow недостаточно — переполнение прячется внутри обновлений остаточной сети. Опасные места:
- Поле
capв структуре ребра. Если суммарный поток через ребро (или сумма параллельных capacity) превышает $2^{31}-1$, само полеcapпереполнится. При пропускных до $10^9$ и сложении на обратных рёбрах это легко. →long long cap. - Переменная
push(минимум по пути). Она берёт минимум остаточных, но участвует вflow += pushи вcap -= push. Еслиpushобъявленаint, а складываете вlong long flow— само сложение ок, ноcap -= pushприint capсломается. → делайте всю арифметику остатков вlong long. - Инициализация
INT_MAXкак «бесконечность» дляpush/dist. При суммировании нескольких рёбер с большими cap или в MCMF (dist[v] + e.cost)INT_MAXпереполнится. БеритеLLONG_MAXи осторожно с переполнением при сложении (не складывайтеLLONG_MAX + cost).
Правило: в потоке делайте cap, flow, push, бесконечности и (в MCMF) стоимости/расстояния все long long. Цена — копейки по памяти, а баг ловится крайне тяжело, потому что на маленьких тестах не проявляется.
struct Edge { int to; long long cap; };
long long push = LLONG_MAX; // не INT_MAX
for(...) push = min(push, e[id].cap);
Для Менгера/паросочетания (единичные cap) int достаточно — там переполнения нет. Опасность именно в задачах с большими пропускными.
Добавлю про MCMF отдельно — там переполнение коварнее. Стоимость пути копится как cost += push * dist[t]. Если push до $10^9$, а dist[t] до $10^9$, то произведение до $10^{18}$ — на грани long long, а сумма по многим путям может и вылезти. Считайте максимум суммарной стоимости заранее и при сомнении используйте __int128 для аккумулятора стоимости. Также при сравнении dist[v] + e.cost < dist[e.to] следите, чтобы dist[v] не был «бесконечностью» — иначе сложение LLONG_MAX + cost переполнится и даст ложное обновление. Защита: пропускать релаксацию, если dist[v] == LLONG_MAX.