数论——Yet Another Subarray Problem

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(rl+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]kceil(1.0(rl)/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);
}

这道题说明了二元的式子是可以化简的,关键是找出最小值

你可能感兴趣的:(数论,例题)