[2019杭电多校第三场][hdu6606]Distribution of books(线段树&&dp)

时间:2023-03-09 08:23:43
[2019杭电多校第三场][hdu6606]Distribution of books(线段树&&dp)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6606

题意为在n个数中选m(自选)个数,然后把m个数分成k块,使得每块数字之和最大的最小。

求数字和最大的最小一般都是二分,二分后可以dp来判断合法,dp[i]表示第i个数字最大可以在的块数。则$dp[i]=max(dp[j])+1,{sum[i]-sum[j]<=x}$,sum为前缀和,x为二分的值。

但是这样的复杂度O(n2logn),显然不行。

则可以优化一下dp,dp的合法转移条件是sum[i]-sum[j]<=x,移项为sum[j]>=sum[i]-x。所以将前缀和离散化,然后每次在权值线段树上查询大于等于sum[i]-x的位置的最大值,即是每次转移的最优位置。

然后用答案更新sum[i]位置的权值即可。

 #include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
#define lson l,mid,i<<1
#define rson mid+1,r,i<<1|1
using namespace std;
typedef long long ll;
const ll maxn = 2e5 + ;
const ll inf = 1e18;
ll Max[maxn * ];
ll a[maxn], b[maxn];
void up(ll i) {
Max[i] = max(Max[i << ], Max[i << | ]);
}
void build(ll l, ll r, ll i) {
Max[i] = -inf;
if (l == r)
return;
ll mid = l + r >> ;
build(lson);
build(rson);
}
void update(ll pos, ll val, ll l, ll r, ll i) {
if (l == r) {
Max[i] = val;
return;
}
ll mid = l + r >> ;
if (pos <= mid)update(pos, val, lson);
else update(pos, val, rson);
up(i);
}
ll query(ll L, ll R, ll l, ll r, ll i) {
if (L > R)return -inf;
if (L <= l && r <= R)
return Max[i];
ll ans = -inf;
ll mid = l + r >> ;
if (L <= mid)ans = max(ans, query(L, R, lson));
if (R > mid)ans = max(ans, query(L, R, rson));
return ans;
}
ll n, k;
ll dp[maxn];
bool check(ll x, ll len) {
build(, len, );
for (ll i = ; i <= n; i++) {
ll pos = lower_bound(b + , b + + len, a[i] - x) - b;
ll pos2 = lower_bound(b + , b + + len, a[i]) - b;
dp[i] = query(pos, len, , len, ) + ;
if (a[i] <= x)
dp[i] = max(dp[i], 1ll);
if (dp[i] >= k)
return true;
if (dp[i] > )
update(pos2, dp[i], , len, );
}
return false;
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
ll x;
scanf("%lld%lld", &n, &k);
for (ll i = ; i <= n; i++)
scanf("%lld", &x), a[i] = a[i - ] + x, b[i] = a[i];
sort(b + , b + + n);
ll len = unique(b + , b + + n) - b - ;
ll l = -inf, r = inf, ans;
while (l <= r) {
ll mid = l + r >> ;
if (check(mid, len)) {
ans = mid;
r = mid - ;
}
else
l = mid + ;
}
printf("%lld\n", ans);
}
}