题目大意:(我翻译的开森)
n个餐馆排成一条直线,相邻两个餐馆有个距离;m张餐票,每张最多能用一次,并且在不同的店使用收益不同。起点终点任意,问总收益减去总路程最大是多少。
n≤5000,m≤200
n
≤
5000
,
m
≤
200
题解:区间确定的时候每张餐票会用在最划算的那个餐馆,反过来对每个餐票和每个餐厅考虑选择哪些区间会使得这张餐票用在这家餐馆。显然是左右遇到更大的之前都可以。为避免重复计数钦定对一个区间和一张餐票若有多解则选最左的就可以了,然后就是一个矩形加和最后问单点最值。暴力差分即可,事实上线段树可以nlgn。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<utility>
#define lint long long
#define gc getchar()
#define mp make_pair
#define fir first
#define sec second
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define clr(a,n) memset(a,0,sizeof(int)*((n)+1))
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
inline int inn()
{
int x,ch;while((ch=gc)<'0'||ch>'9');
x=ch^'0';while((ch=gc)>='0'&&ch<='9')
x=(x<<1)+(x<<3)+(ch^'0');return x;
}
#define N 5010
#define M 210
lint s[N],v[N][N];int b[M][N];int L[N],R[N],stc[N];
int main()
{
int n=inn(),m=inn();rep(i,2,n) s[i]=s[i-1]+inn();
rep(i,1,n) rep(j,1,m) b[j][i]=inn();
rep(i,1,m)
{
int top=0;
for(int j=1;j<=n;j++)
{
while(top&&b[i][j]>b[i][stc[top]]) R[stc[top--]]=j-1;
stc[++top]=j;
}
rep(j,1,top) R[stc[j]]=n;
top=0;
for(int j=n;j>=1;j--)
{
while(top&&b[i][j]>=b[i][stc[top]]) L[stc[top--]]=j+1;
stc[++top]=j;
}
rep(j,1,top) L[stc[j]]=1;
rep(j,1,n) v[L[j]][j]+=b[i][j],
v[L[j]][R[j]+1]-=b[i][j],
v[j+1][j]-=b[i][j],
v[j+1][R[j]+1]+=b[i][j];
// debug(i)ln;rep(j,1,n) debug(j)sp,debug(b[i][j])sp,debug(L[j])sp,debug(R[j])ln;
}
lint ans=0ll;
rep(i,1,n) rep(j,1,n)
{
v[i][j]=v[i][j]+v[i-1][j]+v[i][j-1]-v[i-1][j-1];
if(i<=j) ans=max(ans,v[i][j]-(s[j]-s[i]));
}
return !printf("%lld\n",ans);
}