「CTSC2010」产品销售

「CTSC2010」产品销售

30pts的费用流都会吧...

100pts只要模拟费用流就行了,是不是很简单呀(

咕咕咕

\(M_i\)表示\(i-1\to i\)的正向边,\(M_i^{'}\)表示反向边

\(C_i\)表示\(i \to i-1\)的正向边,\(C_i^{'}\)表示反向边

依次枚举\(1,\cdots,n\)

当前枚举到\(i\),要使\(i\rightarrow t\)满流

两种决策:\(s \rightarrow j \to j+1 \to \cdots \to i\) or \(i \leftarrow i+1 \leftarrow \cdots \leftarrow j \leftarrow s\)

下面表述中,起始位置就是\(j\)

第二种决策

由于是依次枚举,故\(M_i+1,\cdots,M_j\)并没有流量,则费用为\(\sum_{k=i+1}^j cost[C_k]\)

由于\(i\)是顺次推过来,故可以直接对\(\sum_{k=1}^j cost[C_k]\)排序,跳过\(k\leq i || U[k]==0\)的点,取最优即可。

第一种决策

对于当前决策,若有一条边\(C_k( j+1\leq k \leq i)\)有流量,那么费用需要减去\(cost[C_k]+cost[M_k]\)

可行流量为\(min(w[C_k])(w[c_k] \geq 1)\)

那么可以转化一下:在加入\(k\)这个点的时候,对于始于\([1,k-1]\)的第一种决策的路径,若\(C_k\)有流量,那么全部减去\(cost[C_k]\),否则加上\(cost[M_k]\)

\(C_k\)流量减为0时,对始于\([1,k-1]\)的第一种决策的路径全部加上\(cost[M_k]+cost[C_k]\)

显然对于\(C_k\)修改只会执行2次。


具体讲讲怎么维护吧...

线段树an1维护\(w[C_x]\),an2维护以\(j\)为起始到 当前枚举的点\(i\)的花费。

由于\(an1\)要维护\(min(w[C_x])(w[C_x] \geq1)\),为了方便,初始值设为\(inf\)

第一种决策

an2查询\([1,i]\)的最小值\(Min\),并找到其位置\(k\)

an1查询\([k+1,i]\)\(Minf\)= \(min(w[C_x])(w[C_x] \geq1)\) ,并找到其位置。

可行的流量\(flow\)\(min(U[k],D[i],Minf)\)

将an1的\([k+1,i]\)全部减去flow,\(U[k]-=flow,D[i]-=flow\)

对于an1中\(w[C_k]==0\)的点,权值设置为\(inf\),并对an2中\([1,\cdots ,k-1]\)的点加上\(cost[M_k]+cost[C_k]\)

\(U[k]==0\),在an2中设置\(k\)权值为\(inf\)

第二种决策

起始位置为\(k\),可行流量为\(flow=min(U[k],D[i])\)

首先要将an1\([i+1,k]\)当中权值为\(inf\)的点修改为0,用并查集跳过不是\(inf\)的点即可。

然后将an1\([i+1,k]\)权值加\(flow\)\(U[k]-=flow,D[i]-=flow\)

枚举到下一个点i+1

\(i+1\)加入an2中(若\(U[i+1]==0\)设为\(inf\)

在an2中,\([1,i]\)加上\(w[C_{i+1}] \geq 1?-cost[C_{i+1}]:cost[M_{i+1}]\)


其实可以发现an1,an2支持的操作是一样的

#include <bits/stdc++.h>
//#pragma GCC target("avx,avx2,sse4.2")
#define rep(q, a, b) for (int q = a, q##_end_ = b; q <= q##_end_; ++q)
#define dep(q, a, b) for (int q = a, q##_end_ = b; q >= q##_end_; --q)
#define mem(a, b) memset(a, b, sizeof a)
#define debug(a) cerr << #a << ' ' << a << "___" << endl
using namespace std;
// char buf[10000000], *p1 = buf, *p2 = buf;
#define Getchar() \
    getchar()  // p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 10000000, stdin), p1 == p2) ? EOF : *p1++
void in(int &r) {
    static char c;
    r = 0;
    bool flag(0);
    while (c = Getchar(), c < 48) (c == '-') && (flag = 1);
    do
        r = (r << 1) + (r << 3) + (c ^ 48);
    while (c = Getchar(), c > 47);
    flag && (r = -r);
}

const int mn = 100005;
const int INF = 2e9;
int n, D[mn], U[mn], P[mn], M[mn], C[mn];
struct node {
    int x, v;
    bool operator<(const node &A) const { return v < A.v; }
} an1[mn];
struct segment_tree {
    int at[mn << 2], addv[mn << 2], y_1, y_2, ad_v, minv[mn << 2];
    void build(int o, int l, int r, int v) {
        minv[o] = v, at[o] = l;
        if (l == r)
            return;
        int mid = l + r >> 1;
        build(o << 1, l, mid, v);
        build(o << 1 | 1, mid + 1, r, v);
    }
    void maintain(int o) {
        if (minv[o << 1] + addv[o << 1] < minv[o << 1 | 1] + addv[o << 1 | 1]) {
            minv[o] = minv[o << 1] + addv[o << 1];
            at[o] = at[o << 1];
        } else {
            minv[o] = minv[o << 1 | 1] + addv[o << 1 | 1];
            at[o] = at[o << 1 | 1];
        }
    }
    void change(int o, int l, int r, int adv) {
        adv += addv[o];
        if (l == r)
            minv[o] = ad_v - adv;
        else {
            int mid = l + r >> 1;
            if (y_1 <= mid)
                change(o << 1, l, mid, adv);
            else
                change(o << 1 | 1, mid + 1, r, adv);
            maintain(o);
        }
    }
    void change(int x, int v) {
        y_1 = x, ad_v = v;
        change(1, 1, n, 0);
    }
    void modify(int o, int l, int r) {
        if (y_1 <= l && r <= y_2)
            addv[o] += ad_v;
        else {
            int mid = l + r >> 1;
            if (y_1 <= mid)
                modify(o << 1, l, mid);
            if (y_2 > mid)
                modify(o << 1 | 1, mid + 1, r);
            maintain(o);
        }
    }
    void add(int l, int r, int v) {
        if (l > r)
            return;
        y_1 = l, y_2 = r, ad_v = v;
        modify(1, 1, n);
    }

    int Min, At;
    void ask(int o, int l, int r, int adv) {
        adv += addv[o];
        if (y_1 <= l && r <= y_2) {
            if (minv[o] + adv < Min)
                Min = minv[o] + adv, At = at[o];
            return;
        }
        int mid = l + r >> 1;
        if (y_1 <= mid)
            ask(o << 1, l, mid, adv);
        if (y_2 > mid)
            ask(o << 1 | 1, mid + 1, r, adv);
    }
    int ask(int l, int r) {
        if (l > r)
            return INF;
        y_1 = l, y_2 = r, Min = INF;
        ask(1, 1, n, 0);
        return Min;
    }
} an[2];
int fa[mn];
int find(int x) { return !fa[x] ? x : fa[x] = find(fa[x]); }
signed main() {
    freopen("product.in", "r", stdin);
    freopen("product.out", "w", stdout);
    in(n);
    rep(q, 1, n) in(D[q]);
    rep(q, 1, n) in(U[q]);
    rep(q, 1, n) in(P[q]);
    rep(q, 2, n) in(M[q]);
    rep(q, 2, n) in(C[q]);
    int sm = 0;
    rep(q, 2, n) sm += C[q], an1[q] = { q, sm + P[q] };
    sort(an1 + 2, an1 + n + 1);
    int tp = 2;
    an[0].build(1, 1, n, INF);
    an[1].build(1, 1, n, 0);
    long long ans = 0;
    sm = 0;
    rep(q, 1, n) {
        an[1].add(1, q - 1, an[0].ask(q, q) > 1e9 ? M[q] : -C[q]);
        sm += C[q];
        an[1].change(q, U[q] ? P[q] : INF);
        while (D[q]) {
            while (tp <= n && (an1[tp].x <= q || !U[an1[tp].x])) ++tp;
            if (tp > n || an[1].ask(1, q) <= an1[tp].v - sm) {
                int v = an[1].ask(1, q);
                int at = an[1].At, vl = an[0].ask(at + 1, q);
                int lim = min(min(D[q], U[at]), vl);
                ans += 1LL * lim * v;
                while (vl == lim) {
                    int at1 = an[0].At;
                    an[1].add(1, at1 - 1, C[at1] + M[at1]);
                    an[0].change(at1, INF);
                    vl = an[0].ask(at + 1, q);
                }
                an[0].add(at + 1, q, -lim);
                D[q] -= lim, U[at] -= lim;
                if (!U[at])
                    an[1].change(at, INF);
            } else {
                int at = an1[tp].x;
                int mid = at, lim = min(U[at], D[q]);
                U[at] -= lim, D[q] -= lim;
                while (find(mid) >= q + 1) {
                    an[0].change(find(mid), 0);
                    fa[find(mid)] = find(mid) - 1;
                }
                an[0].add(q + 1, at, lim);
                ans += 1LL * lim * (an1[tp].v - sm);
            }
        }
    }
    printf("%lld\n", ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/klauralee/p/11358663.html