「Libre OJ」6014「网络流 24 题」最长 k 可重区间集题解

主要思路

这道题还是很妙的,我只想到了最大费用最大流之后就想不动了。

我们在这里介绍边数为\(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;
}

 

Leave a Reply

Your email address will not be published. Required fields are marked *