主要思路
这道题还是很妙的,我只想到了最大费用最大流之后就想不动了。
我们在这里介绍边数为\(O(n)\)级别的解法。考虑把所有的端点离散化为\(2n\)个连续的点,然后相邻端点\(i, i+1\)相互连接流量无限、费用为零的边。考虑区间左右端点,左端点连到右端点,流量为\(1\),费用为区间长度。最后源点连左端点,汇点连到右端点。
这样就可以强制满流,且费用最大。
代码
// LOJ6014.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 2e5 + 200, INF = 0x3f3f3f3f;
int head[MAX_N], current, pre[MAX_N], dist[MAX_N];
int st, ed, flow[MAX_N], n, k, mx_range;
bool vis[MAX_N];
vector<int> vec;
struct segment
{
int l, r;
} segs[MAX_N];
struct edge
{
int to, weight, cost, nxt;
} edges[MAX_N << 1];
void addpath(int src, int dst, int weight, int cst)
{
edges[current].to = dst, edges[current].weight = weight;
edges[current].cost = cst, edges[current].nxt = head[src];
head[src] = current++;
}
void addtube(int src, int dst, int weight, int cst)
{
addpath(src, dst, weight, cst);
addpath(dst, src, 0, -cst);
}
bool bfs()
{
memset(dist, 0x3f, sizeof(dist)), memset(vis, false, sizeof(vis));
queue<int> q;
q.push(st), vis[st] = true, flow[st] = INF, dist[st] = 0;
while (!q.empty())
{
int u = q.front();
vis[u] = false;
q.pop();
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (edges[i].weight > 0 && dist[edges[i].to] > dist[u] + edges[i].cost)
{
dist[edges[i].to] = dist[u] + edges[i].cost;
flow[edges[i].to] = min(flow[u], edges[i].weight);
pre[edges[i].to] = i;
if (!vis[edges[i].to])
vis[edges[i].to] = true, q.push(edges[i].to);
}
}
return dist[ed] != INF;
}
int mcmf()
{
long long ans = 0;
while (bfs())
{
int p = ed, i = pre[p];
ans += 1LL * flow[ed] * dist[ed];
while (p != st)
{
edges[i].weight -= flow[ed], edges[i ^ 1].weight += flow[ed];
p = edges[i ^ 1].to, i = pre[p];
}
}
return ans;
}
int main()
{
memset(head, -1, sizeof(head));
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++)
{
scanf("%d%d", &segs[i].l, &segs[i].r);
if (segs[i].l > segs[i].r)
swap(segs[i].l, segs[i].r);
vec.push_back(segs[i].l), vec.push_back(segs[i].r);
}
vec.push_back(-0x3f3f3f3f);
sort(vec.begin(), vec.end());
st = 0, ed = 2 * n + 1;
addtube(st, 1, k, 0);
for (int i = 1; i < 2 * n; i++)
addtube(i, i + 1, INF, 0);
addtube(2 * n, ed, k, 0);
for (int i = 1; i <= n; i++)
{
int len = segs[i].r - segs[i].l;
segs[i].l = lower_bound(vec.begin(), vec.end(), segs[i].l) - vec.begin();
segs[i].r = lower_bound(vec.begin(), vec.end(), segs[i].r) - vec.begin();
addtube(segs[i].l, segs[i].r, 1, -len);
}
printf("%d", -mcmf());
return 0;
}