Subarray
这题很明显直接扫不行,考虑优化,既然有式子,考虑先化简。
∑ i = l r a [ i ] − k ∗ ( c e i l ( 1.0 ∗ ( r − l + 1 ) / m ) ) \sum_{i=l}^ra[i]-k*(ceil(1.0*(r-l+1)/m)) ∑i=lra[i]−k∗(ceil(1.0∗(r−l+1)/m))令l=l-1
原式= s u m [ r ] − s u m [ l ] − k ∗ c e i l ( 1.0 ∗ ( r − l ) / m ) sum[r]-sum[l]-k*ceil(1.0*(r-l)/m) sum[r]−sum[l]−k∗ceil(1.0∗(r−l)/m)
l=m*(l/m)+(l%m),r=m*(r/m)+(r%m)
这时就可以再化简式子
原式=(sum[r]-k*(r/m))-(sum[l]-k*(l/m))-kceil(1.0(r%m-l%m)%m)
发现前面的一段可以做前缀和,后面的一段可以单独处理,因为l的同余系的(l%m)的值一定,所以维护前缀sum[l]-k*(l/m)的最小值即可
#include //本题是经典的dp优化题
using namespace std;
typedef long long ll;
const ll N=300010;
ll INF=12345678900;
ll n;
ll m,k;ll a[N],pre[N],sum=0,minj[15],ans=0;
ll MAX(ll x,ll y){
return x>y?x:y;
}
ll MIN(ll x,ll y){
return x<y?x:y;
}
int main(){
scanf("%lld%lld%lld",&n,&m,&k);
for(ll i=1;i<=n;i++){scanf("%lld",&a[i]);sum+=a[i],pre[i]=sum-k*(i/m);}
for(ll j=0;j<m;j++) minj[j]=INF;//不开long long 会炸
minj[0]=0;//因为令l=l-1,所以l可取0,0的初始值为0
for(ll i=1;i<=n;i++)
{
ll f=-INF;
for(ll j=0;j<m;j++){
ll tmp=ceil(1.0*((i%m)-j)/m);//ceil的内置函数是float,所以不能直接取max
//1.0*(i%m-j)/m,不是1.0*((i%m-j)/m)
f=MAX(f,pre[i]-minj[j]-k*tmp);;
}
minj[i%m]=MIN(minj[i%m],pre[i]);
ans=max(ans,f);
}
printf("%lld",ans);
}
#include
using namespace std;
typedef long long ll;
const int N=300010;
ll pre[N],minj[N];
int a[N];
int n,m,k;
ll sum=0,INF=12345678900;
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum+=1ll*a[i];
pre[i]=sum-1ll*k*(i/m);
}
for(int i=0;i<m;i++) minj[i]=INF;
minj[0]=0;
ll ans=0;
for(int i=1;i<=n;i++){
for(int j=0;j<m;j++){
int tmp=ceil(1.0*(i%m-j)/m);
ans=max(ans,pre[i]-minj[j]-1ll*tmp*k);
}
minj[i%m]=min(minj[i%m],pre[i]);
}
printf("%lld",ans);
}
这道题说明了二元的式子是可以化简的,关键是找出最小值