bzoj4276

线段树优化建图+费用流

朴素的做法是每个强盗直接对每个区间的每个点连边,然后跑最大权匹配,这样有5000*5000条边,肯定过不去,那么我们用线段树优化一下,因为线段树能把一个O(n)的区间划分为O(logn)段

然后就建一棵线段树,每个节点向两个儿子连(inf,0)的边,叶子结点连向sink,(1,0),每个强盗向对应区间节点连边,这样边数就将为了nlogn条。据说正解是贪心?

抄了个板子

 
#include<bits/stdc++.h>
using namespace std;
const int N = 30010, inf = 0x3f3f3f3f;
struct edge {
    int nxt, to, f, c;
} e[N * 100];
int n, m, k, source, sink, tot, cnt = 1, sum;
int head[N], pree[N], prev[N], vis[N], d[N];
inline void link(int u, int v, int f, int c)
{
    e[++cnt].nxt = head[u];
    head[u] = cnt;
    e[cnt].f = f;
    e[cnt].to = v;
    e[cnt].c = c;
}
inline void insert(int u, int v, int f, int c)
{
    link(u, v, f, c);
    link(v, u, 0, -c);
}
bool spfa()
{
    memset(d, -1, sizeof(d));
    d[source] = 0;
    queue<int> q;
    q.push(source);
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for(int i = head[u]; i; i = e[i].nxt) if(e[i].f && (d[e[i].to] < d[u] + e[i].c || d[e[i].to] == -1))
        {
            pree[e[i].to] = i;
            prev[e[i].to] = u;
            d[e[i].to] = d[u] + e[i].c;
            if(vis[e[i].to] == 0)
            {
                q.push(e[i].to);
                vis[e[i].to] = 1;
            }
        }
    }
    return d[sink] != -1; 
}
inline int Edmonds_Karp()
{
    int ans = 0;
    while(spfa())
    {
        int now = sink, delta = inf;
        while(now != source)
        {
            delta = min(delta, e[pree[now]].f);
            now = prev[now];
        }
        now = sink;
        while(now != source)
        {
            e[pree[now]].f -= delta;
            e[pree[now] ^ 1].f += delta; 
            now = prev[now];
        }
        ans += delta * d[sink];
    } 
    return ans;
}
void build(int l, int r, int x)
{
    if(l == r) 
    {
        insert(x, sink, 1, 0);
        return;
    }
    int mid = (l + r) >> 1;
    build(l, mid, x << 1);
    build(mid + 1, r, x << 1 | 1);
    insert(x, x << 1, inf, 0);
    insert(x, x << 1 | 1, inf, 0);
}
void update(int l, int r, int x, int a, int b, int c, int pos)
{
    if(l > b || r < a) return;
    if(l >= a && r <= b) 
    {
        insert(pos, x, 1, c);
        return;
    }
    int mid = (l + r) >> 1;
    update(l, mid, x << 1, a, b, c, pos);
    update(mid + 1, r, x << 1 | 1, a, b, c, pos);
}
int main()
{
    scanf("%d", &n);
    sink = 20000 + n + 1;
    build(1, 5000, 1);
    for(int i = 1; i <= n; ++i) 
    {
        int l, r, c;
        scanf("%d%d%d", &l, &r, &c);
        insert(source, i + 20000, 1, 0);
        update(1, 5000, 1, l, r - 1, c, i + 20000); 
    }   
    printf("%d
", Edmonds_Karp());
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/19992147orz/p/7529922.html