题目链接:点我啊╭(╯^╰)╮
题目大意:
n n n 本书, k k k 个学生,每本书的权值可能为负
每个学生的快乐为他所获得的书的权值和
每个学生获得的书的编号必须是连续的
分书也必须按学生的编号顺序,最后可以剩下书
问最后所有学生最大的快乐值最小为多少??
解题思路:
二分快乐值 x x x,则问 n n n 个人能否在满足 x x x 的前提下分成 k k k 组
d p [ i ] dp[i] dp[i] 为到第 i i i 个人最多可以分的组数, p r e pre pre 为前缀
d p [ i ] = m a x ( d p [ j ] ) + 1 dp[i] = max(dp[j]) + 1 dp[i]=max(dp[j])+1 p r e [ i ] ? p r e [ j ] ≤ x pre[i] - pre[j] ≤ x pre[i]?pre[j]≤x
这是个 n 2 n^2 n2 的 d p dp dp,发现可以用数据结构优化
先离散化 p r e [ i ] pre[i] pre[i],然后按照离散化后的 p r e pre pre 数组建权值线段树
枚举 i i i,要找到 p r e [ j ] ≥ p r e [ i ] ? x pre[j] ≥ pre[i]-x pre[j]≥pre[i]?x 的最大的 d p [ j ] dp[j] dp[j]
则只需要在权值线段树里找 ( p r e [ i ] ? x , m a x ) (pre[i]-x,max) (pre[i]?x,max) 里的最大值即可
时间复杂度: O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)
核心:用离散化后的值建权值线段树
#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 10;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
int T, n, k, cnt;
ll pre[maxn], ls[maxn];
int t[maxn<<2], a[maxn], id[maxn];void build(int l,int r,int rt){if(l==r){t[rt] = 0;return ;}int m = l + r >> 1;build(lson);build(rson);t[rt] = max(t[rt<<1], t[rt<<1|1]);
}void update(int pos,int c,int l,int r,int rt){if(l==r){t[rt] = max(c, t[rt]);return;}int m = l + r >> 1;if(pos<=m) update(pos,c,lson);else update(pos,c,rson);t[rt] = max(t[rt<<1], t[rt<<1|1]);
}int query(int L,int R,int l,int r,int rt){if(L<=l && r<=R) return t[rt];int m = l + r >> 1;int ret = -1e9;if(L<=m) ret = max(ret, query(L,R,lson));if(R>m) ret = max(ret, query(L,R,rson));return ret;
}bool check(ll m){build(1,n,1);for(int i=1; i<=n; i++){int p = lower_bound(ls+1, ls+cnt+1, pre[i]-m) - ls;int q = query(p,n,1,n,1);if(q) update(id[i],q+1,1,n,1);else if(pre[i]<=m) update(id[i],1,1,n,1);}return query(1,n,1,n,1)>=k;
}int main() {scanf("%d", &T);while(T--){scanf("%d%d", &n, &k);for(int i=1; i<=n; i++){scanf("%d", a+i);ls[i] = pre[i] = pre[i-1] + a[i];}sort(ls+1, ls+n+1);cnt = unique(ls+1, ls+n+1) - ls - 1;for(int i=1; i<=n; i++)id[i] = lower_bound(ls+1, ls+cnt+1, pre[i]) - ls;ll l = -1e15, r = 1e15;while(l<=r){ll mid = l + r >> 1;if(check(mid)) r = mid - 1;else l = mid + 1;}printf("%lld\n", l);}
}