AtCoder Regular Contest 067 F - Yakiniku Restaurants 单调栈+差分dp

7 篇文章 0 订阅

题意

i-1到i的距离为a[i],你可以往两个方向走,然后你有m张票,第i个店用第j张票的美味值是b[i][j],总的开心值=美味值-距离
问总的开心值的最大值
2N5×103 2 ≤ N ≤ 5 × 103
1M200 1 ≤ M ≤ 200
1Ai109 1 ≤ A i ≤ 109
1Bi,j109 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<xiyr 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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值