原题
题面
具体实现讲解
首先想到用
s
u
m
sum
sum记录
a
a
a数组的前缀和,把每种和弦都试一遍,很明显会超时。
定义
c
a
l
(
s
,
l
,
r
)
cal(s,l,r)
cal(s,l,r)代表以
s
s
s为左端点,右端点在
l
l
l到
r
r
r的范围内,能得到的最大和弦美妙度。对于一个右端点
p
p
p,美妙度为
s
u
m
p
−
s
u
m
s
−
1
sum_p-sum_{s-1}
sump−sums−1,
s
u
m
s
−
1
sum_{s-1}
sums−1确定,只要求
s
u
m
p
sum_p
sump的最大值,于是用
S
T
ST
ST表维护
s
u
m
sum
sum的
R
M
Q
RMQ
RMQ(最大值)。
用优先队列维护
c
a
l
(
s
,
l
,
r
)
cal(s,l,r)
cal(s,l,r)的最大值,只需要取出
k
k
k个,就可以得到答案。初始时放入所有
c
a
l
(
i
,
i
+
L
−
1
,
i
+
R
−
1
)
cal(i,i+L-1,i+R-1)
cal(i,i+L−1,i+R−1),由于可能出现两个被选中的和弦位于同一个起点,在取出
c
a
l
(
s
,
l
,
r
)
cal(s,l,r)
cal(s,l,r)后要放入
c
a
l
(
s
,
l
,
p
s
−
1
)
cal(s,l,ps-1)
cal(s,l,ps−1)和
c
a
l
(
s
,
p
s
+
1
,
r
)
cal(s,ps+1,r)
cal(s,ps+1,r)(
p
s
ps
ps为取到最大的
s
u
m
sum
sum的位置)。
S
T
ST
ST表不仅要维护最大值,还要维护最大值的位置;
c
a
l
cal
cal同样需要返回最优解位置,可以将返回类型定为pair<int,int>
;优先队列中的节点要用结构体存储(
s
s
s,
l
l
l,
r
r
r,
p
s
ps
ps和
v
a
l
val
val),重载运算符
<
<
< 即可。
代码
#include<bits/stdc++.h>
using namespace std;
#define maxlog 19
int n,k,L,R,a[500005],sum[500005],Log[500005],cnt;
pair<int,int> ST[500005][20];
long long ans;
struct node{
int s,l,r,val,ps;
bool operator < (const node &x) const{ return val<x.val; }
//priority_queue默认为大根堆,所以return val<x.val
} t;
priority_queue<node,vector<node> > q;
pair<int,int> cal(int s,int l,int r){
int k=Log[r-l+1];
if(ST[l][k]>ST[r-(1<<k)+1][k])
return make_pair(ST[l][k].first-sum[s-1],ST[l][k].second);
else return make_pair(ST[r-(1<<k)+1][k].first-sum[s-1],ST[r-(1<<k)+1][k].second);
}
int main(){
scanf("%d%d%d%d",&n,&k,&L,&R);
Log[1]=0;
for(int i=2;i<=n;i++) Log[i]=Log[i/2]+1;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
ST[i][0].first=sum[i];ST[i][0].second=i;
}
for(int j=1;j<=maxlog;j++)
for(int i=1;i+(1<<j)-1<=n;i++){
if(ST[i][j-1].first>ST[i+(1<<j-1)][j-1].first) ST[i][j]=ST[i][j-1];
else ST[i][j]=ST[i+(1<<(j-1))][j-1];
}
for(int i=1;i+L-1<=n;i++){
t.s=i,t.l=i+L-1,t.r=min(i+R-1,n);
pair<int,int> p=cal(t.s,t.l,t.r);
t.val=p.first,t.ps=p.second;
q.push(t);
}
while(!q.empty()&&cnt<k){
t=q.top();q.pop();
// cout<<t.s<<' '<<t.l<<' '<<t.r<<' '<<t.val<<' '<<t.ps<<endl;
ans+=t.val;cnt++;
int p=t.ps,tr=t.r,tl=t.l;
if(p>tl){
t.r=p-1;
pair<int,int> pp=cal(t.s,t.l,t.r);
t.val=pp.first,t.ps=pp.second;
q.push(t);
}
if(p<tr){
t.l=p+1;t.r=tr;
pair<int,int> pp=cal(t.s,t.l,t.r);
t.val=pp.first,t.ps=pp.second;
q.push(t);
}
}
printf("%lld\n",ans);
return 0;
}
此文主要用于纪念,如果讲解不够详细,可以转至洛谷题解,大佬很多。(我也是看了题解才做出来的)