欢迎使用皮肤 Geek|

Tsawke

园龄:2年7个月粉丝:6关注:2

SP685 SEQPAR - Partition the sequence 题解

SP685 SEQPAR - Partition the sequence Solution

更好的阅读体验戳此进入

(建议您从上方链接进入我的个人网站查看此 Blog,在 Luogu 中图片会被墙掉,部分 Markdown 也会失效)

题面

给定 n 个数的数列 an,给定 k,将数列分成 k 段,满足每一段的元素和不大于 m,求最小的 $ m T $ 组数据。

1kn1.5×104,3×104ai3×104,1T10

Solution

首先二分答案很显然,二分 m,并验证 m 是否对于 k 成立。

然后对于验证 m 是否成立,我们显然可以想到,对于这样的一个数列和 m,有解的情况下,一定存在一个最小的和最大的块数的划分方案,并且解是存在连续性的,对于最小 fmn 和最大 fmx 一定有 k[fmn,fmx] 都成立,这个的严谨证明我不会很显然,就不证明了。

The proof is left to the reader.

然后对于求 fmn(i)fmx(i),令其为考虑前 i 个数的合法方案的最小和最大的 k,求个前缀和 sum(i),显然有如下的 DP 方程:

fmn(i)=min(fmn(j)+1)j<isum(j)sum(i)mfmx(i)=max(fmx(j)+1)j<isum(j)sum(i)m

对于二分的范围,不难想到我们令数列中负数的和为 ξ,正数的和为 ϵ,那么范围即为 [ξ,ϵ],我们令这个区间长度为 v

发现这个东西的复杂度是 O(n2) 的,最终复杂度 O(Tn2logv),显然会寄,于是考虑优化 DP。

显然我们发现这个转移时可以优化的,可以考虑写个平衡树然后区间取最大最小值等,但是我不会不好写,于是我们考虑线段树。

普通线段树显然开不下,所以我们要写一个动态开点的权值线段树,其中的叶子节点下标表示 sum(i) 的值,存的是 fmn(i)fmx(i) 的值,然后支持单点修改和区间求 minmax。考虑在每次验证的时候新开一个线段树,然后每次查询 [sum(i)m,inf] 区间的 minmax 然后 +1 作为新的 fmn(i)fmx(i),然后再把这两个值插到线段树中对应的 sum(i) 中,具体实现可以看一下代码,这道题还是挺巧妙的。

对于权值线段树的范围,显然与二分限制相同。

那么最终复杂度为 O(Tnlognlogv)

Code

#define _USE_MATH_DEFINES
#include <bits/extc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW(arr) void* Edge::operator new(size_t){static Edge* P = arr; return P++;}

using namespace std;
using namespace __gnu_pbds;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

#define INF 0x3f3f3f3f

template<typename T = int>
inline T read(void);

struct Node{
    Node *ls, *rs;
    int fmn, fmx;
    OPNEW;
}nd[30000000];
void* Node::operator new(size_t){static Node* P = nd; return P++;}
int limMin, limMax;
int N, K;
int a[18000];
int sum[18000];
int fmn[18000], fmx[18000];
Node* rt;
class SegTree{
    private:
        #define MID ((gl + gr) >> 1)
        #define SIZ (gr - gl + 1)
    public:
        void Pushup(Node* p){
            if(!p)return;
            p->fmn = min(p->ls ? p->ls->fmn : INF, p->rs ? p->rs->fmn : INF),
            p->fmx = max(p->ls ? p->ls->fmx : -INF, p->rs ? p->rs->fmx : -INF);
        }
        void Modify(int idx, pair < int, int > val, Node* &p = rt, int gl = limMin, int gr = limMax){
            if(!p)p = new Node{npt, npt, INF, 0};
            if(gl == gr)return tie(p->fmn, p->fmx) = val, void();
            if(idx <= MID) Modify(idx, val, p->ls, gl, MID);
            else Modify(idx, val, p->rs, MID + 1, gr);
            Pushup(p);
        }
        pair < int, int > Query(int l, int r, Node* p = rt, int gl = limMin, int gr = limMax){
            // printf("Querying l = %d, r = %d, gl = %d, gr = %d, val = %d,%d\n", l, r, gl, gr, p ? p->fmn : -114514, p ? p->fmx : 114514);
            if(!p)return {INF, -INF};
            if(l <= gl && gr <= r)return {p->fmn, p->fmx};
            auto L = MID >= l ? Query(l, r, p->ls, gl, MID) : make_pair(INF, -INF);
            auto R = MID + 1 <= r ? Query(l, r, p->rs, MID + 1, gr) : make_pair(INF, -INF);
            return {min(L.first, R.first), max(L.second, R.second)};
        }
}st;
bool Check(int M){
    rt = new Node{npt, npt, INF, -INF};
    fmx[0] = fmn[0] = 0;
    st.Modify(sum[0], {fmn[0], fmx[0]});
    for(int i = 1; i <= N; ++i){
        tie(fmn[i], fmx[i]) = st.Query(sum[i] - M, INF);
        ++fmn[i], ++fmx[i];
        st.Modify(sum[i], {fmn[i], fmx[i]});
    }
    if(fmn[N] <= K && K <= fmx[N])return true;
    return false;
}
int main(){
    int T = read();
    while(T--){
        limMin = limMax = 0;
        N = read(), K = read();
        for(int i = 1; i <= N; ++i)
            a[i] = read(),
            a[i] >= 0 ? limMax += a[i] : limMin += a[i],
            sum[i] = sum[i - 1] + a[i];
        int l = limMin, r = limMax, ans(-1);
        while(l <= r){
            int mid = (l + r) >> 1;
            if(Check(mid))ans = mid, r = mid - 1;
            else l = mid + 1;
        }
        printf("%d\n", ans);
    }
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template<typename T>
inline T read(void){
    T ret(0);
    short flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

UPD

update-2022_10_13 初稿

本文作者:Tsawke

本文链接:https://www.cnblogs.com/tsawke/p/16786801.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Tsawke  阅读(20)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起