这是一道典型的网络流题目。我们只要对于每个SPF值向汇点连一条容量为这个SPF值个数的边,然后源点向每头牛连一条容量为1的边,每头牛向每个可用的SPF值连一条容量为1的边,求最大流即可。
这里还有一个问题就是如果按照上述方式建图可能或有C*1000条边这个数量级是2e6的有可能TLE,我们需要建边优化。
我们发现每头牛的可达SPF值是一个连续的区间有可能有1000个具体节点,我们通过建立一种类似于线段树的网络流模型来使一头牛的可达SPF值为log(1000)个。
具体算法是先建立一颗区间为[1,1000]的线段树,然后每个叶子节点[l,l]向汇点连SPF值为l的个数的边,然后每个非叶子节点分别向其左右儿子连容量为INF的边。这样每个牛的可达SPF就可以用类似线段树的方式进行连边,仅需要log(1000)条边即可。
代码:
#include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <queue> using namespace std; #define lson rt * 2 #define rson rt * 2 + 1 typedef long long ll; const ll INF = 1e16; const int N = 2e5 + 100; struct edge { ll to, cap, rev; edge() {} edge(ll to, ll cap, ll rev) :to(to), cap(cap), rev(rev) {} }; vector<edge> G[N]; ll level[N], iter[N]; void add_edge(ll from, ll to, ll cap) { G[from].push_back(edge(to, cap, G[to].size())); G[to].push_back(edge(from, 0, G[from].size() - 1)); } void bfs(ll s) { memset(level, -1, sizeof(level)); queue<int> que; level[s] = 0; que.push(s); while (!que.empty()) { ll v = que.front(); que.pop(); for (ll i = 0; i < G[v].size(); i++) { edge &e = G[v][i]; if (e.cap > 0 && level[e.to] < 0) { level[e.to] = level[v] + 1; que.push(e.to); } } } } ll dfs(ll v, ll t, ll f) { if (v == t) return f; for (ll &i = iter[v]; i < G[v].size(); i++) { edge &e = G[v][i]; if (e.cap > 0 && level[v] < level[e.to]) { ll d = dfs(e.to, t, min(f, e.cap)); if (d > 0) { e.cap -= d; G[e.to][e.rev].cap += d; return d; } } } return 0; } ll max_flow(ll s, ll t) { ll flow = 0; while (true) { bfs(s); if (level[t] < 0) return flow; memset(iter, 0, sizeof(iter)); ll f; while ((f = dfs(s, t, INF)) > 0) { flow += f; } } } int n, m, cnt = 2, S = 1, T = 2; ll sa[N], aa[N], bb[N], id[N * 4]; void build(int l, int r, int rt) { id[rt] = ++cnt; if (l == r) { add_edge(cnt, T, sa[l]); return; } int m = (l + r) / 2; build(l, m, lson); add_edge(id[rt], id[lson], INF); build(m + 1, r, rson); add_edge(id[rt], id[rson], INF); } void insert(int rl, int rr, int idx, int l, int r, int rt) { if (rl == l && rr == r) { add_edge(idx, id[rt], 1); return; } int m = (l + r) / 2; if (rr <= m) insert(rl, rr, idx, l, m, lson); else if (m < rl) insert(rl, rr, idx, m + 1, r, rson); else { insert(rl, m, idx, l, m, lson); insert(m + 1, rr, idx, m + 1, r, rson); } } int main() { int a, b; scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) scanf("%d%d", aa + i, bb + i); for (int i = 1; i <= m; i++) { scanf("%d%d", &a, &b); sa[a] += b; } build(1, 1000, 1); for (int i = 1; i <= n; i++) { add_edge(S, ++cnt, 1); insert(aa[i], bb[i], cnt, 1, 1000, 1); } printf("%lld\n", max_flow(S, T)); return 0; }