前言
solve
- 我们考虑 \(k=1\),这个情况就是只找一个数
sbmqwm:说了滚木
- 我们令一个左端点为 \(i\),那其长度范围在 \([L+i-1,min(R+i-1,n)]\),令 \(s\) 表示整个序列的前缀和,设最优解下标为 \(v\),那么我们的最优解为:
$s[v]-s[i-1],(L \le v \le R)$
- 其实最优解就是在 \([L+i-1,min(R+i-1,n)]\) 的前缀数组最大值的位置
- 这个 \(v\) 可以用 \(ST\) 表 \(O(1)\) 求出,\(ST\) 表维护前缀最大值就行
- 当然,\(v\) 的下标还要维护,这很好办,把 \(ST\) 表的数组开成 \(pair\) 就行,由于 \(pair\) 默认按第一关键字排序,所以\(ST\) 表处理时就不用管,就是别在查询的时候搞反就行
我不会告诉你我搞反了的
- 当然你也可以用 \(ST\) 表纯维护下标,然后在算的时候套一个 \(a\) 就行
- 这样我们解决了 \(k=1\) 的情况,现在扩展
- 我们枚举左端点,第一个最优(最大)的解一定会在这些集合中,考虑下一个最优解出现的位置
- 会有两种情况:
- 在上面没被选到的集合中
- 在选到的这个集合中(当然,除去选到最优的点)
- 这样就很好搞了
- 我们直接开一个堆,向里面塞一个 \(5\) 元组,分别代表这个候选解的左端点位置,取值范围(两个),最优解出现位置,答案(前缀数组相减),按答案从大到小排序
- sbmqwm:我有两个__
- msjing:你没有
- 所以我们先枚举所有左端点,算一下,全放堆里,相当于是固定左端点找右端点最优,是一种候补思想
- 然后弹堆,取最大的,这个点会把取值范围分成两段,分别计算,加堆,重复
- 由于每次贪心那最大,这样解最优
- 就是要特判一些情况
- 弹出的候选决策的取值范围 \(l>r\),不合法,\(k\) 不消耗,跳过
- 弹出的候选决策的取值范围 \(l=r\),合法但是加堆里会使答案错掉,以防万一我们加上贡献,\(k\) 消耗,不加堆,跳过
我不会告诉你我在算第一种情况时把 \(k--\) 了的
- 其他没啥了
- 这题是个比较常见的 \(trick\),
今天模拟赛还打到了但是msjing认为是用 CDQ + 主席树,想通了很容易了
- 代码里的 \(freopen\) 是打模拟赛剩的忘删了
点击查看代码
#include <bits/stdc++.h>
#define int long long
#define fi first
#define se second
using namespace std;
constexpr int maxn=5e5+10;
int read()
{
int x=0,f=1;
char ch=getchar();
while (ch<'0' || ch>'9')
{
if (ch == '-') f=-1;
ch=getchar();
}
while (ch>='0' && ch<='9')
{
x=(x<<1)+(x<<3)+ch-'0';
ch=getchar();
}
return x*f;
}
int n,k,L,R;
int a[maxn];
namespace ST
{
int lg[maxn];
pair<int,int> f[maxn][20];
void Log() {for (int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;}
void st()
{
for (int j=1;j<=lg[n];j++)
for (int i=1;i<=n-(1<<j)+1;i++)
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
}using namespace ST;
struct _
{
int id,l,r,mxid,sum;
bool operator < (const _ &a) const
{
return sum<a.sum;
}
};
priority_queue<_> q;
signed main()
{
// freopen("B.in","r",stdin);
// freopen("B.out","w",stdout);
n=read(),k=read(),L=read(),R=read();
for (int i=1;i<=n;i++) a[i]=read();
for (int i=1;i<=n;i++) f[i][0].fi=f[i-1][0].fi+a[i],f[i][0].se=i;
Log(),st();
for (int i=1;i<=n;i++)
{
int l=L+i-1,r=min(R+i-1,n);
// cerr << l << " " << r << endl;
if (l>n) break;
int k=lg[r-l+1];
pair<int,int> v=max(f[l][k],f[r-(1<<k)+1][k]);
// cerr << v.fi << " " << v.se << endl;
q.push({i,l,r,v.se,v.fi-f[i-1][0].fi});
}
int ans=0;
while (k)
{
_ nd=q.top();q.pop();
// cerr << nd.sum << endl;
if (nd.l>nd.r) continue;
ans+=nd.sum;
k--;
if (nd.l == nd.r) continue;
// cerr << nd.id << " " << nd.l << " " << nd.r <<
// " " << nd.mxid << " " << nd.sum << endl;
int l1=nd.l,r1=nd.mxid-1,l2=nd.mxid+1,r2=nd.r;
int k1=lg[r1-l1+1],k2=lg[r2-l2+1];
pair<int,int> v1=max(f[l1][k1],f[r1-(1<<k1)+1][k1]),v2=max(f[l2][k2],f[r2-(1<<k2)+1][k2]);
// cerr << "---------------" << endl;
// cerr << v1.fi << " " << v1.se << endl;
// cerr << v2.fi << " " << v2.se << endl;
// cerr << "--------------" << endl;
q.push({nd.id,l1,r1,v1.se,v1.fi-f[nd.id-1][0].fi});
q.push({nd.id,l2,r2,v2.se,v2.fi-f[nd.id-1][0].fi});
}
printf("%lld\n",ans);
return 0;
}