把下界约束建成 0/1 边,SCC 判严格环后在缩点 DAG 上求最长路。
OJ: luogu
题目 ID: P3275
难度:提高
标签:差分约束强连通分量DAGpython
日期: 2026-07-17 03:00
题意
满足五类大小关系且每人至少一颗糖,求最小糖果总数。
思路
约束写成 x_v >= x_u+w,其中严格大于是权 1,否则权 0。若同一 SCC 内存在权 1 边,就形成正环而无解;否则缩点图是 DAG,从每个分量初值 1 做最长路,分量内节点取相同最小值。
Python 知识
- 显式栈实现 Kosaraju 两遍 DFS,避免递归深度问题。
deque做缩点 DAG 的拓扑排序。- 分量大小乘分量最长路值,直接得到总糖果数。
代码
python
import sys
from collections import deque
input = sys.stdin.buffer.readline
n, constraints = map(int, input().split())
graph = [[] for _ in range(n + 1)]
reverse_graph = [[] for _ in range(n + 1)]
edges = []
def add_edge(u, v, weight):
graph[u].append(v)
reverse_graph[v].append(u)
edges.append((u, v, weight))
possible = True
for _ in range(constraints):
kind, a, b = map(int, input().split())
if kind == 1:
add_edge(a, b, 0)
add_edge(b, a, 0)
elif kind == 2:
possible &= a != b
add_edge(a, b, 1)
elif kind == 3:
add_edge(b, a, 0)
elif kind == 4:
possible &= a != b
add_edge(b, a, 1)
else:
add_edge(a, b, 0)
if possible:
visited = bytearray(n + 1)
finish = []
for start in range(1, n + 1):
if visited[start]:
continue
visited[start] = 1
stack = [(start, 0)]
while stack:
node, index = stack[-1]
if index < len(graph[node]):
neighbor = graph[node][index]
stack[-1] = node, index + 1
if not visited[neighbor]:
visited[neighbor] = 1
stack.append((neighbor, 0))
else:
finish.append(node)
stack.pop()
component = [0] * (n + 1)
component_count = 0
for start in reversed(finish):
if component[start]:
continue
component_count += 1
component[start] = component_count
stack = [start]
while stack:
node = stack.pop()
for neighbor in reverse_graph[node]:
if component[neighbor] == 0:
component[neighbor] = component_count
stack.append(neighbor)
size = [0] * (component_count + 1)
dag = [[] for _ in range(component_count + 1)]
indegree = [0] * (component_count + 1)
for node in range(1, n + 1):
size[component[node]] += 1
for u, v, weight in edges:
first, second = component[u], component[v]
if first == second:
if weight:
possible = False
break
else:
dag[first].append((second, weight))
indegree[second] += 1
if not possible:
print(-1)
else:
value = [1] * (component_count + 1)
queue = deque(node for node in range(1, component_count + 1) if indegree[node] == 0)
while queue:
node = queue.popleft()
for neighbor, weight in dag[node]:
value[neighbor] = max(value[neighbor], value[node] + weight)
indegree[neighbor] -= 1
if indegree[neighbor] == 0:
queue.append(neighbor)
print(sum(value[node] * size[node] for node in range(1, component_count + 1)))原有 C++ 版本仍保留:
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-07-17 01:40
* update_at: 2026-07-17 01:40
*/
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
return 0;
}复杂度
SCC 与 DAG DP 均为 O(n+m),空间 O(n+m)。
总结
0/1 下界约束中,矛盾恰好是强连通分量里出现严格增长边。