嘘~ 正在从服务器偷取页面 . . .

CF280D k-Maximum Subsequence Sum 题解


CF280D k-Maximum Subsequence Sum 题解

题目链接:k-Maximum Subsequence Sum

题意

给定长度为 \(n\) 的数列和 \(m\) 个询问,要求支持:

  1. 修改某个位置的值。

  2. 询问区间 \([l,r]\) 里选出至多 \(k\) 个不相交的子段和的最大值。

数据范围

\(1 \le n,m \le 10^5,~|a_i| \le 500,~0\le k \le 20\)

初学模拟费用流,好像没想象中那么难懂。

考虑以下费用流模型:

  • \(S\) 向每个 \(i\) 连边,容量为 \(1\) ,费用为 \(0\) ,其中 \(1 \le i \le n\)
  • 每个 \(i\)\(i + 1\) 连边,容量为 \(1\) ,费用为 \(a_i\) ,其中 \(1 \le i < n\)
  • 每个 \(i\)\(T'\) 连边,容量为 \(1\) ,费用为 \(0\) ,其中 \(1 \le i \le n\)
  • \(T'\)\(T\) 连边,容量为 \(c\) ,费用为 \(0\) ,其中 \(0 \le c \le k\)

那么问题就变成了对于每个 \(c\)\(S\)\(T\) 的最大费用最大流。

不过这个费用流模型直接跑显然是复杂度爆炸的,这就引出模拟费用流

模拟费用流的基本思想是用数据结构等工具,去模拟费用流的增广操作

可以发现,每次增广只会增加 \(1\) 的流量,即选择一个 \(i\) 出发。

由于我们不会选择同起点/终点的区间,所以每次增广只会增加一个区间。

而退流的操作,相当于把他们交集部分取反,再求正常的答案。

比如第一次选择了区间 \([1,4]\) ,我们把它取反

这样第二次如果选择了区间 \([3,6]\) ,得到的答案就是区间 \([1,2]\)\([5,6]\)

由于 \(k\) 很小,所以重复 \(k\) 次,就可以得到答案了。如果增广的贡献为 \(0\) 可以直接退出循环。

现在我们需要快速维护区间最大子段和、区间取反操作。

后者可以通过打 tag 和维护区间最小子段和来实现

另外由于我们取反了一些区间,所以我们还得记录取反了哪些区间,询问完再翻回来。

时间复杂度 \(\mathcal{O}(qk\log n)\)

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x, int y) { x < y ? x = y : 0; }
void down(int &x, int y) { x > y ? x = y : 0; }
#define rep(i, a, b) for(int i = (a), i##END = (b); i <= i##END; i++)
#define Rep(i, a, b) for(int i = (a), i##END = (b); i >= i##END; i--)
#define N ((int)(1e5 + 15))
#define ls(at) ((at) << 1)
#define rs(at) ((at) << 1 | 1)

int n, a[N];
struct rec { int l, r, s; };
rec operator+(const rec& x, const rec& y) { return {x.l, y.r, x.s + y.s}; }
bool operator<(const rec &x, const rec& y) { return x.s < y.s; }
void rev(rec& x) { x.s = -x.s; }
struct node { rec max, min, lmax, rmax, lmin, rmin, sum; bool tag; } tr[N * 4];
void init(int x, int k, int at)
{
    tr[at].max = {x, x, k}; tr[at].lmax = {x, x, k}; tr[at].rmax = {x, x, k};
    tr[at].min = {x, x, k}; tr[at].lmin = {x, x, k}; tr[at].rmin = {x, x, k};
    tr[at].sum = {x, x, k}; tr[at].tag = 0;
}
node merge(const node &x, const node &y)
{
    node res;
    res.max = max({x.max, y.max, x.rmax + y.lmax});
    res.min = min({x.min, y.min, x.rmin + y.lmin});
    res.lmax = max(x.lmax, x.sum + y.lmax);
    res.rmax = max(y.rmax, x.rmax + y.sum);
    res.lmin = min(x.lmin, x.sum + y.lmin);
    res.rmin = min(y.rmin, x.rmin + y.sum);
    res.sum = x.sum + y.sum; res.tag = 0; return res;
}
void push_up(int at) { tr[at] = merge(tr[ls(at)], tr[rs(at)]); }
void proc(int at)
{
    swap(tr[at].max, tr[at].min);
    swap(tr[at].lmax, tr[at].lmin); swap(tr[at].rmax, tr[at].rmin);
    rev(tr[at].max); rev(tr[at].min);
    rev(tr[at].lmax); rev(tr[at].rmax);
    rev(tr[at].lmin); rev(tr[at].rmin);
    rev(tr[at].sum); tr[at].tag ^= 1;
}
void push_down(int at)
{
    if(tr[at].tag)
    {
        proc(ls(at)); proc(rs(at));
        tr[at].tag = 0;
    }
}
void modify(int x, int k, int l = 1, int r = n, int at = 1)
{
    if(l == r) return init(l, k, at);
    push_down(at); int mid = (l + r) >> 1;
    if(x <= mid) modify(x, k, l, mid, ls(at));
    else modify(x, k, mid + 1, r, rs(at));
    push_up(at);
}
void update(int nl, int nr, int l = 1, int r = n, int at = 1)
{
    if(nl <= l && r <= nr) return proc(at);
    push_down(at); int mid = (l + r) >> 1;
    if(nl <= mid) update(nl, nr, l, mid, ls(at));
    if(nr > mid) update(nl, nr, mid + 1, r, rs(at));
    push_up(at);
}
node query(int nl, int nr, int l = 1, int r = n, int at = 1)
{
    if(nl <= l && r <= nr) return tr[at];
    push_down(at); int mid = (l + r) >> 1;
    if(nl > mid) return query(nl, nr, mid + 1, r, rs(at));
    if(nr <= mid) return query(nl, nr, l, mid, ls(at));
    return merge(query(nl, nr, l, mid, ls(at)), query(nl, nr, mid + 1, r, rs(at)));
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n; rep(i, 1, n) { int x; cin >> x; modify(i, x); }
    int qwq, op, x, y, k; cin >> qwq;
    while(qwq--)
    {
        cin >> op;
        if(op == 0) { cin >> x >> k; modify(x, k); }
        else
        {
            cin >> x >> y >> k;
            int res = 0; static queue<node> q; q = {};
            while(k--)
            {
                auto t = query(x, y); if(t.max.s < 0) break;
                res += t.max.s; update(t.max.l, t.max.r); q.push(t);
            }
            cout << res << '\n';
            while(!q.empty())
            {
                auto t = q.front(); q.pop();
                update(t.max.l, t.max.r);
            }
        }
    }
    return 0;
}

参考文献

[1] https://www.luogu.com.cn/article/cigabgyb


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录