题意
i-1到i的距离为a[i],你可以往两个方向走,然后你有m张票,第i个店用第j张票的美味值是b[i][j],总的开心值=美味值-距离
问总的开心值的最大值
2≤N≤5×103
2
≤
N
≤
5
×
103
1≤M≤200
1
≤
M
≤
200
1≤Ai≤109
1
≤
A
i
≤
109
1≤Bi,j≤109
1
≤
B
i
,
j
≤
109
分析
首先肯定是走了不会折回,考虑左端点和右端点分别在哪
假设当前是第i家店,用第j张票的美味值为b[i][j],你可以做两次单调栈找到在这个店用这张餐卷最好的范围
假设找到两个端点
b[l][j]≤b[i][j]≤b[r][j]
b
[
l
]
[
j
]
≤
b
[
i
]
[
j
]
≤
b
[
r
]
[
j
]
的l和r,那么对于区间(x,y),
l<x≤i≤y≤r
l
<
x
≤
i
≤
y
≤
r
,肯定在第i家店用餐
然后就可以把这个东西看成是一个二维的平面,现在要支持一个矩阵+-和单点询问
我们可以用差分来+-,然后累加来单点询问
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll N = 5010;
const ll M = 210;
inline ll read()
{
ll p=0; ll f=1; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
ll a[N],b[N][M]; ll s[N]; ll top; ll sum[N][N]; ll l[N],r[N]; ll pre[N];
int main()
{
ll n=read(),m=read();
for(ll i=2;i<=n;i++) a[i] = read();
for(ll i=1;i<=n;i++) for(ll j=1;j<=m;j++) b[i][j] = read();
for(ll i=1;i<=m;i++)
{
top = 0; s[top] = 0;
for(ll j=1;j<=n;j++) l[j] = r[j] = 0;
for(ll j=1;j<=n;j++)
{
while(top && b[j][i] > b[s[top]][i]) top--;
l[j] = s[top] + 1; s[++top] = j;
}
top = 0; s[top] = n+1;
for(ll j=n;j>=1;j--)
{
while(top && b[j][i] > b[s[top]][i]) top--;
r[j] = s[top] - 1; s[++top] = j;
}
for(ll j=1;j<=n;j++)
{
// prllf("%d %d %d\n",i,l[j],r[j]);
sum[l[j]][j] += b[j][i];
sum[j+1][j] -= b[j][i] ;
sum[l[j]][r[j]+1] -= b[j][i];
sum[j+1][r[j]+1] += b[j][i];
}
}
for(ll i=2;i<=n;i++) pre[i] = pre[i-1] + a[i];
ll maxx = 0;
for(ll i=1;i<=n;i++) for(ll j=1;j<=n;j++)
{
sum[i][j] = sum[i][j] + sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1];
if(i<=j) maxx = max(maxx , sum[i][j] - pre[j] + pre[i]);
// prllf("%d %d %d\n",i,j,sum[i][j]);
}
return printf("%lld\n",maxx),0;
}