Live2D

Solution -「ZJOI 2013」「洛谷 P3337」防守战线

Description

  Link.

  有 n 个位置,从左至右编号 1n。在第 i 个位置放一座塔的代价为 ci,一个位置可以放任意数量的塔。给定 m 个要求,第 i 个表示 [li,ri] 内至少有 di 座塔。求最小的代价和。

  n103,其余参数 104

Solution

  经历了逝量的 whk 学习,我学会了背套路。(

  原问题可以写成线规:

min    z=i=1nci(xixi1)s.t.    xixi1xrixli1di,

而它显然等价于:

min    z=i=1n(cici+1)xi+i=1nImax{xi1xi,0}+i=1mImax{dixri+xli1,0}.

​其中 I 是一个足够大的常数。

  我们的论文套路是,对于以下线规:

min    z=ubuxu+u,vcuvmax{0,xvxuwuv},

其对应最小费用流模型:

  • bu 表示 u 点的流出量-流入量。

    为了流量守恒,对于 bu>0u,连 S,u,bu,0

    对于 bu<0u,连 u,T,bu,0

  • cuv 表示 u,v 的容量,wuv 表示 u,v 的费用,所以连边 u,v,cuv,wuv

该图的最小费用最大流的费用的相反数就是答案。

  点的系数出减入,max 外是容量,max 内是费用,减数连向被减数,最小费用相反数!


  但 whk 是落后的,我们来证明。令 fuv 表示 u,v 的流量,考虑在上述条件下的最小费用流形式:

min    z=u,vcuvfuvs.t.    fuvcuvvfvuvfuv=bu.

puv 表示第一类限制的对偶,qu 表示第二类限制的对偶,得到:

max    z=ubuqu+u,vcuvpuvs.t.    qvqupuvwuv.

那么这个 puv 非常的自由,直接给它定成最优,所以

min    z=ubuqu+u,vcuvmax{0,qvquwuv}

的相反数就是答案。 

Code

  总之得写势能 Dijkstra。

/*+Rainybunny+*/

#include <bits/stdc++.h>

#define rep(i, l, r) for (int i = l, rep##i = r; i <= rep##i; ++i)
#define per(i, r, l) for (int i = r, per##i = l; i >= per##i; --i)

typedef std::pair<int, int> PII;
#define fi first
#define se second

const int MAXN = 1e3, MAXM = 1e4, IINF = 0x3f3f3f3f;
int n, m, c[MAXN + 5], l[MAXM + 5], r[MAXM + 5], d[MAXM + 5];

namespace FlowGraph {

const int MAXND = MAXN + 3, MAXEG = MAXN * 2 + MAXM;
int ecnt = 1, S, T, head[MAXND + 5], curh[MAXND + 5];
int hig[MAXND + 5], dis[MAXND + 5];
bool instk[MAXND + 5];
struct Edge { int to, flw, cst, nxt; } graph[MAXEG * 2 + 5];

inline void link(const int s, const int t, const int f, const int c) {
    graph[++ecnt] = { t, f, c, head[s] }, head[s] = ecnt;
    graph[++ecnt] = { s, 0, -c, head[t] }, head[t] = ecnt;
}

inline bool spfa() {
    static bool inq[MAXND + 5]; static std::queue<int> que;
    rep (i, 0, T) dis[i] = IINF;
    dis[S] = 0, que.push(S);
    while (!que.empty()) {
        int u = que.front(); que.pop(), inq[u] = false;
        for (int i = head[u], v; i; i = graph[i].nxt) {
            if (graph[i].flw && dis[u] + graph[i].cst < dis[v = graph[i].to]) {
                dis[v] = dis[u] + graph[i].cst;
                if (!inq[v]) inq[v] = true, que.push(v);
            }
        }
    }
    return dis[T] != IINF;
}
inline bool dijkstra() {
    static std::priority_queue<PII, std::vector<PII>, std::greater<PII> > heap;
    rep (i, 0, T) hig[i] += dis[i], dis[i] = IINF;
    heap.push({ dis[S] = 0, S });
    while (!heap.empty()) {
        PII p(heap.top()); heap.pop();
        if (dis[p.se] != p.fi) continue;
        for (int i = head[p.se], v; i; i = graph[i].nxt) {
            int d = p.fi + graph[i].cst + hig[p.se] - hig[v = graph[i].to];
            if (graph[i].flw && dis[v] > d) heap.push({ dis[v] = d, v });
        }
    }
    return dis[T] != IINF;
}

inline PII augment(const int u, int iflw) {
    if (u == T) return { iflw, 0 };
    PII ret(0, 0); instk[u] = true;
    for (int &i = curh[u], v; i; i = graph[i].nxt) {
        if (graph[i].flw && !instk[v = graph[i].to]
          && dis[v] == dis[u] + hig[u] - hig[v] + graph[i].cst) {
            PII tmp(augment(v, std::min(iflw, graph[i].flw)));
            ret.fi += tmp.fi, ret.se += graph[i].cst * tmp.fi + tmp.se;
            iflw -= tmp.fi, graph[i].flw -= tmp.fi, graph[i ^ 1].flw += tmp.fi;
            if (!iflw) break;
        }
    }
    if (ret.fi) instk[u] = false;
    return ret;
}

inline PII dinic() {
    PII ret(0, 0);
    for (spfa(); dijkstra(); ) {
        rep (i, 0, T) curh[i] = head[i], instk[i] = false;
        PII tmp(augment(S, IINF));
        ret.fi += tmp.fi, ret.se += tmp.se;
    }
    return ret;
}

} using namespace FlowGraph;

int main() {
    scanf("%d %d", &n, &m);
    rep (i, 1, n) scanf("%d", &c[i]);
    rep (i, 1, m) scanf("%d %d %d", &l[i], &r[i], &d[i]);

    S = n + 1, T = n + 2;
    rep (i, 0, n) {
        if (c[i] > c[i + 1]) link(S, i, c[i] - c[i + 1], 0);
        else link(i, T, c[i + 1] - c[i], 0);
    }
    rep (i, 0, n - 1) link(i + 1, i, IINF, 0);
    rep (i, 1, m) link(r[i], l[i] - 1, IINF, -d[i]);

    printf("%d\n", -dinic().se);
    return 0;
}

posted @   Rainybunny  阅读(146)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示