T T T 组样例,每组样例两个数 n , k n, k n,k 表示下面 n n n 本书要分给 k k k 个学生,每本书有一个权值 x , − 1 0 9 ≤ x ≤ 1 0 9 x, -10^9 \le x \le 10^9 x,−109≤x≤109 ,每个学生分到的书必须是一段连续的区间,且每个学生至少分得一本书。你可以选择任意长度的后缀不参与分配,但是必须保证分配合法。问在保证分配合法的情况下学生分配到的书权加和最大的最小值是多少。
1 < = T < = 10 1<=T<=10 1<=T<=10
1 < = n < = 2 ∗ 105 1<=n<=2*105 1<=n<=2∗105
1 < = k < = n 1<=k<=n 1<=k<=n
− 1 0 9 < = a i < = 1 0 9 -10^9<=ai<=10^9 −109<=ai<=109
样例输入
2
4 2
3 -2 4 -2
5 4
-1 -1 -1 -1 6
样例输出
2
-1
考虑二分答案然后 c h e c k check check, 容易想到 O ( n k ) \mathcal{O}(nk) O(nk) 的 d p dp dp 转移:
d p [ i ] = m a x ( d p [ i ] , d p [ j ] + 1 ) , s u m [ i ] − m i d ≤ s u m [ j ] \begin{aligned} dp[i] = max(dp[i],~dp[j]+1),~~sum[i] - mid \le sum[j] \end{aligned} dp[i]=max(dp[i], dp[j]+1), sum[i]−mid≤sum[j] 其中的 s u m sum sum 为原数列的前缀和。
这样写显然时间复杂度不允许,然后考虑优化,容易发现我们的转移只需要最大的 d p [ j ] dp[j] dp[j] 转移过来即可,所以对 s u m sum sum 离散化后线段树维护 c h e c k check check 即可。时间复杂度为 O ( n l o g n ∗ l o g ( 二 分 ) ) \mathcal{O}(nlogn*log(二分)) O(nlogn∗log(二分)), 注意一下二分的上下界是 [ − 2 ∗ 1 0 14 , 2 ∗ 1 0 14 ] [-2*10^{14}, 2*10^{14}] [−2∗1014,2∗1014]。
#include
using namespace std;
typedef long long ll;
#define lson ((rt)<<1)
#define rson ((rt)<<1|1)
const int maxn = 4e5+5;
vector<ll> v;
int n, k;
int a[maxn];
ll sum[maxn];
int li[maxn];
struct node{
int mx;
}seg[maxn<<2];
void build(int rt, int l, int r){
seg[rt].mx = 0;
if(l == r){
return;
}
int mid = (l + r)>>1;
build(lson, l, mid);
build(rson, mid+1, r);
}
void update(int rt, int l, int r, int x, int val){
if(l == x && r == x){
seg[rt].mx = max(seg[rt].mx, val);
return;
}
int mid = (l+r)>>1;
if(mid >= x) update(lson, l, mid, x, val);
else update(rson, mid+1, r, x, val);
seg[rt].mx = max(seg[lson].mx, seg[rson].mx);
}
int query(int rt, int l, int r, int L, int R){
if(L <= l && r <= R){
return seg[rt].mx;
}
int mid = (l+r)>>1;
int res = 0;
if(L <= mid) res = query(lson, l, mid, L, R);
if(mid < R) res = max(res, query(rson, mid+1, r, L, R));
return res;
}
bool check(ll mid){
int H = v.size();
build(1, 1, H);
for(int i = 1; i <= n; i++){
int pos = lower_bound(v.begin(), v.end(), sum[i] - mid) - v.begin()+1;
if(pos > H){
if(sum[i] <= mid) update(1, 1, H, li[i], 1);
}
else{
int mx = query(1, 1, H, pos, H);
if(mx == 0){
if(sum[i] <= mid) update(1, 1, H, li[i], 1);
}
else{
update(1, 1, H, li[i], mx+1);
}
}
}
return query(1, 1, H, 1, H) >= k;
}
int main()
{
int T;
scanf("%d", &T);
while(T--){
v.clear();
scanf("%d %d", &n, &k);
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
sum[i] = sum[i-1] + a[i];
v.push_back(sum[i]);
}
sort(v.begin(), v.end());
v.erase(unique(v.begin(), v.end()), v.end());
for(int i = 1; i <= n; i++){
li[i] = lower_bound(v.begin(), v.end(), sum[i]) - v.begin() + 1;
}
ll l = -2e14-5, r = 2e14+5;
while(l < r){
ll mid = (l+r)>>1;
if(check(mid)) r = mid;
else l = mid+1;
}
printf("%lld\n", l);
}
return 0;
}