2019杭电多校第三场hdu6606 Distribution of books(二分答案+dp+权值线段树)

Distribution of books

题目传送门

解题思路

求最大值的最小值,可以想到用二分答案。

对于二分出的每个mid,要找到是否存在前缀可以份为小于等于mid的k份。先求出这n个数的前缀和sum[],dp[i]表示前i个可以构成的最大份数。初始化dp[1~n]为-1,dp[0]为0,转移方程式为:dp[i] = max(dp[j]) + 1,(sum[i] - sum[j] <= mid, dp[j] >= 0, 0 <= j < i)。如果有一个dp[i]>=k,说明mid可以,r=mid,否则l=mid+1。

但是数据范围很大,直接这么找肯定超时。所以我们要用离散化后的权值线段树来维护。对于i,我们要找的是满足sum[i] - sum[j] <= mid,即sum[j] >= sum[i] - mid的j里最大的dp[j]。所以我们用权值线段树来来维护前i-1个dp的最大值。对于每次二分都清空然后重新建树,按照插入dp[i],对于每次询问只需返回离散化后sum[j]~最大值的范围内的最大dp值。

代码如下

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;

inline int read(){
    int res = 0, w = 0; char ch = 0;
    while(!isdigit(ch)){
        w |= ch == '-', ch = getchar();
    }
    while(isdigit(ch)){
        res = (res << 3) + (res << 1) + (ch ^ 48);
        ch = getchar();
    }
    return w ? -res : res;
}

const int N = 200005;

ll a[N], b[N];
struct T{
    int l, r;
    int maxx;
}tree[N<<2];
int dp[N];
int n, m;

void build(int k, int l, int r)
{
    tree[k].l = l;
    tree[k].r = r;
    tree[k].maxx = -1;
    if(l >= r)
        return;
    int mid = (l + r) / 2;
    build(2*k, l, mid);
    build(2*k+1, mid + 1, r);
}

void insert(int k, int x, int u)
{
    if(tree[k].l == tree[k].r){
        tree[k].maxx = max(tree[k].maxx, u);
        return;
    }
    int mid = (tree[k].l + tree[k].r) / 2;
    if(x <= mid)
        insert(2*k, x, u);
    else
        insert(2*k+1, x, u);
    tree[k].maxx = max(tree[2*k].maxx, tree[2*k+1].maxx);
}

int query(int k, int l, int r)
{
    if(tree[k].l >= l && tree[k].r <= r)
        return tree[k].maxx;
    int mid = (tree[k].l + tree[k].r) / 2;
    int m1, m2;
    m1 = m2 = -1;
    if(l <= mid)
        m1 = query(2*k, l, r);
    if(r > mid)
        m2 = query(2*k+1, l, r);
    return max(m1, m2);
}

bool work(ll mid, int k)
{
    build(1, 0, k);  
    insert(1, lower_bound(b, b + k + 1, 0) - b, 0);
    for(int i = 1; i <= n; i ++){
        int l = lower_bound(b, b + k + 1, a[i] - mid) - b;
        if(l != k + 1)
            dp[i] = query(1, l, k)+ 1;
        else
            dp[i] = -1;
        if(dp[i] == 0)
            dp[i] = -1;
        if(dp[i] >= m)
            return true;
        int x = lower_bound(b, b + k + 1, a[i]) - b;
        insert(1, x, dp[i]);
    }
    return false;
}

int main()
{
    int _ = read();
    while(_ --){
        n = read(), m = read();
        ll l = 0, r = 0;
        a[0] = b[0] = 0;
        for(int i = 1; i <= n; i ++){
            ll x = read();
            if(x < 0)
                l += x;
            if(x > 0)
                r += x;
            a[i] = a[i - 1] + x;
            b[i] = a[i];
        }
        sort(b, b + n + 1);
        int k = unique(b, b + n + 1) - b - 1;
        ll mid = (l + r) / 2;
        while(l < r){
            if(work(mid, k))
                r = mid;
            else
                l = mid + 1;
            mid = (l + r) / 2;
        }
        printf("%lld\n", mid);
    }
    return 0;
}
posted @ 2019-07-30 17:08  whisperlzw  阅读(211)  评论(0编辑  收藏  举报