原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=6606
题意
有n本书,你需要把它分成k组,每组必须有一个,而且分组必须是连续的,最后多余的书可以丢掉。每组书的权值和为
s
u
m
i
sum_{i}
sumi,现在我们求
m
a
x
(
s
u
m
1..
s
u
m
k
)
最
小
max(sum1..sumk)最小
max(sum1..sumk)最小
分析
一般求最大值最小或最小值最大的问题都是用二分答案,所以我们往这方面去想。直接设mid为每组的最大值,那么我们则用这个标准取划分组,最后如果分组数大于k,就是符合要求的。
然后考虑怎么求分组的数量,我们设dp[i]代表前i个数最多可以分成的组数,然后转移方程可以很快写出
d
p
[
i
]
=
m
a
x
(
d
p
[
i
]
,
d
p
[
j
]
+
1
)
[
s
u
m
[
i
]
−
s
u
m
[
j
]
<
=
m
i
d
]
dp[i]=max(dp[i], dp[j]+1)[sum[i]-sum[j]<=mid]
dp[i]=max(dp[i],dp[j]+1)[sum[i]−sum[j]<=mid]
如果用朴素方法遍历,那么复杂度是O(N^2)的,所以这时候要套个数据结构维护一下,转化一下思路,我们要找当前可以转移的状态中最大的,那么就是 s u m [ j ] ∈ [ s u m [ i ] − m i d , i n f ] sum[j]∈[sum[i]-mid,inf] sum[j]∈[sum[i]−mid,inf],因此我们建一棵权值线段树去维护区间最大值就可以了,数据范围很大可以先离散化。
Code
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define re register
typedef long long ll;
typedef pair<ll, ll> PII;
typedef unsigned long long ull;
const int N = 2e5 + 10, M = 1e6 + 5, INF = 0x3f3f3f3f;
const int MOD = 1e9+7;
struct node {
int l, r;
int maxx;
}t[N<<2];
ll sum[N], a[N];
int dp[N];
vector<ll> ve;
void push_up(int u) {
t[u].maxx = max(t[u<<1].maxx, t[u<<1|1].maxx);
}
void build(int u, int l, int r) {
t[u].l = l, t[u].r = r, t[u].maxx = 0;
if (l == r) return;
int mid = (l + r) >> 1;
build(u<<1, l, mid);
build(u<<1|1, mid+1, r);
push_up(u);
}
void modify(int u, int pos, int val) {
if (t[u].l == t[u].r) {
t[u].maxx = max(t[u].maxx, val);
return;
}
int mid = (t[u].l + t[u].r) >> 1;
if (pos <= mid) modify(u<<1, pos, val);
else modify(u<<1|1, pos, val);
push_up(u);
}
int query(int u, int ql, int qr) {
if (ql <= t[u].l && qr >= t[u].r) return t[u].maxx;
int mid = (t[u].l + t[u].r) >> 1;
int Max = 0;
if (ql <= mid) Max = max(query(u<<1, ql, qr), Max);
if (qr > mid) Max = max(query(u<<1|1, ql, qr), Max);
return Max;
}
int n, m;
bool check(ll mid) {
build(1, 1, ve.size());
for (int i = 1; i <= n; i++) {
int pos = lower_bound(ve.begin(), ve.end(), sum[i] - mid) - ve.begin() + 1;
int now = lower_bound(ve.begin(), ve.end(), sum[i]) - ve.begin() + 1;
int Max = query(1, pos, ve.size());
if (!Max) {
if (sum[i] > mid) dp[i] = 0;
else dp[i] = 1;
} else {
dp[i] = Max + 1;
}
modify(1, now, dp[i]);
}
return query(1, 1, ve.size()) >= m;
}
void solve() {
int T; cin >> T; while (T--) {
cin >> n >> m; ve.clear();
for (int i = 1; i <= n; i++) cin >> a[i], sum[i] = sum[i-1] + a[i], ve.push_back(sum[i]);
sort(ve.begin(), ve.end());
ve.erase(unique(ve.begin(), ve.end()), ve.end());
ll l = -1e15, r = 1e15;
while (l <= r) {
ll mid = (l + r) >> 1;
if (check(mid)) r = mid - 1;
else l = mid + 1;
}
cout << l << endl;
}
}
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
#ifdef ACM_LOCAL
freopen("input", "r", stdin);
freopen("output", "w", stdout);
#endif
solve();
return 0;
}