• 2019HDU多校第三场 Distribution of books 二分 + DP


    题意:给你一个序列,你可以选择序列的一个前缀,把前缀分成k个连续的部分,要求这k个部分的区间和的最大值尽量的小,问这个最小的最大值是多少?

    思路:首先看到最大值的最小值,容易想到二分。对于每个二分值mid,我们判断原序列是否可以构成k个区间和小于等于mid的区间,这个可以用DP来做。我们先求出序列的前缀和,这样可以把区间和变成前缀相减的形式。之后,对于当前的前缀和sum[i], 我们找前缀和大于等于sum[i] - mid的状态中dp值最大的向自己转移。如果最后存在状态的dp值大于等于k,那么说明二分的mid值偏大,要缩小上边界。反之亦然。dp用离散化 + 线段树优化,总复杂度O(n * logn) * log(值域)

    代码:

    #include <bits/stdc++.h>
    #define LL long long
    #define ls (o << 1)
    #define rs (o << 1 | 1)
    using namespace std;
    const int maxn = 200010;
    LL sum[maxn];
    LL a[maxn];
    LL b[maxn];
    int dp[maxn], mx[maxn * 4];
    int n, m, tot;
    void build(int o, int l, int r) {
    	if(l == r) {
    		mx[o] = -1;
    		return;
    	}
    	int mid = (l + r) >> 1;
    	build(ls, l, mid);
    	build(rs, mid + 1, r);
    	mx[o] = max(mx[ls], mx[rs]);
    }
    void update(int o, int l, int r, int p, int val) {
    	if(l == r) {
    		mx[o] = max(mx[o], val);
    		return;
    	}
    	int mid = (l + r) >> 1;
    	if(p <= mid) update(ls, l, mid, p, val);
    	else update(rs, mid + 1, r, p, val);
    	mx[o] = max(mx[ls], mx[rs]);
    }
    int query(int o, int l, int r, int ql, int qr) {
    	if(ql > qr) return -1;
    	if(l >= ql && r <= qr) {
    		return mx[o];
    	}
    	int mid = (l + r) >> 1, ans = -1;
    	if(ql <= mid) ans = max(ans, query(ls, l, mid, ql, qr));
    	if(qr > mid) ans = max(ans, query(rs, mid + 1, r, ql, qr));
    	return ans;
    }
    bool solve(LL mid) {
    	build(1, 1, tot);
    	update(1, 1, tot, lower_bound(b + 1, b + 1 + tot, 0) - b, 0);
    	for (int i = 1; i <= n; i++) {
    		int p = lower_bound(b + 1, b + 1 + tot, sum[i] - mid) - b;
    		int p1 = lower_bound(b + 1, b + 1 + tot, sum[i]) - b;
    		int tmp =  query(1, 1, tot, p, tot);
    		if(tmp == -1) dp[i] = -1;
    		else dp[i] = tmp + 1;
    		if(dp[i] >= m) return 0;
    		update(1, 1, tot, p1, dp[i]);
    	}
    	return 1;
    }
    int main() {
    	int T;
    //	freopen("input.txt", "r", stdin);
    //	freopen("1.in", "r", stdin);
    	scanf("%d", &T);
    	while(T--) {
    		scanf("%d%d", &n, &m);
    		for (int i = 1; i <= n; i++) {
    			scanf("%lld", &a[i]);
    			sum[i] = sum[i - 1] + a[i];
    			b[i] = sum[i];
    		}
    		b[n + 1] = 0;
    		sort(b + 1, b + 1 + n + 1);
    		tot = unique(b + 1, b + 1 + n + 1) - (b + 1);
    		LL l = -2e14, r = 2e14;
    		while(l < r) {
    			LL mid = (l + r) >> 1;
    			if(solve(mid)) l = mid + 1;
    			else r = mid;
    		}
    		printf("%lld
    ", l);
    	}
    } 
    

      

  • 相关阅读:
    JDom写入XML例子
    hdu 2549
    hdu 1328
    hdu 1334
    hdu 2547
    hdu 2374
    hdu 2550
    hdu 1335
    hdu 2548
    hdu 1722
  • 原文地址:https://www.cnblogs.com/pkgunboat/p/11267824.html
Copyright © 2020-2023  润新知