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

时间:2021-10-30 00:36:34

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;
}