题意
大概说一下我理解的题意。。。
给你一个长度为 n n n的序列,你可以随意抛弃一些后缀数字,但必须保证序列至少包含 k k k个数字。
问将这个序列分成连续 k k k段后,问段的权值和最大值最小为多少?
思路
二分,dp验证,线段树优化
二分答案, d p [ i ] dp[i] dp[i]表示前 i i i个数能分成多少段
转移: d p [ i ] = ∑ m a x ( d p [ j ] + 1 ) [ s u m [ i ] − s u m [ j ] ≤ x ] dp[i] = \sum_{max}(dp[j]+1)[sum[i]-sum[j]\le x] dp[i]=∑max(dp[j]+1)[sum[i]−sum[j]≤x]
权值线段树维护前缀和值对应的最大 d p dp dp值,支持单点更新,区间查询即可。
比赛时最初在想 d p dp dp,因为之前刚做过HDU - 1024这个最大 m m m子段和,然后自闭了好久不会优化复杂度。。
后来队友说了句二分,感觉好有道理。。。。
经过一段时间的自闭,感觉好像知道怎么写了,想到了dp验证,线段树优化。。。but我没想到权值线段树,我想成了每个位置维护一个前缀和值和dp权值。。。。然后gg了,比赛代码如下(不过我当时dp写法不一样,思路差不多
AC_Code
vector<LL> vs;
int ok(LL x) {
vs.eb(-1e18);
for(int i = 1; i <= n; ++i) {
res[i] = res[i-1] + ar[i];
vs.eb(res[i]), vs.eb(res[i] - x);
}
my_unique(vs);
build(1, vs.size(), 1);
int flag = 0;
for(int i = 1; i <= n; ++i) {
if(res[i] <= x) dp[i] = 1;
else dp[i] = 0;
int tmp = lower_bound(all(vs), res[i]) - vs.begin();
int las = lower_bound(all(vs), res[i] - x) - vs.begin();
int ret = query(las, vs.size(), 1, vs.size(), 1);
if(ret) dp[i] = big(ret + 1, dp[i]);
update(tmp, dp[i], 1, vs.size(), 1);
flag = max(flag, dp[i]);
}
vs.clear();
return flag >= k;
}