1. 程式人生 > >AtCoder Grand Contest 067 F - Yakiniku Restaurants

AtCoder Grand Contest 067 F - Yakiniku Restaurants

題目傳送門:https://arc067.contest.atcoder.jp/tasks/arc067_d

題目大意:

\(N\)家燒烤店,在直線上按順序排列,第\(i\)家燒烤店和第\(i+1\)家燒烤店的距離為\(A_i\)。你有\(M\)張燒烤券,在第\(i\)家燒烤店使用第\(j\)張券可以獲得\(B_{i,j}\)的快樂,你可以在某家燒烤店使用多張券。你現在可以從某個燒烤店開始,使用所有的券,使得你的快樂值減去所走路程最大

我們考慮每個\(B_{i,j}\)的貢獻,我們找到第一個一個\(B_{L,j}>B_{i,j}\)\(L<i\),然後\(R\)類似,那麼\(B_{i,j}\)

對答案有貢獻需要決策左端點在\((L,i]\)中,右端點在\([i,R)\)

於是我們可以設\(f_{l,r}\)表示決策在\([l,r]\)的收益,對於每個\(B_{i,j}\),我們對\(f_{(L,i],[i,R)}\)加上\(B_{i,j}\)的貢獻,可以證明,對於某張券\(j\)\(B_{1\sim n,j}\)對答案的貢獻矩陣沒有交集,因此我們可以用二維差分解決,最後還原\(f\)即可

/*problem from Wolfycz*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline char gc(){
    static char buf[1000000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline int frd(){
    int x=0,f=1; char ch=gc();
    for (;ch<'0'||ch>'9';ch=gc())   if (ch=='-')    f=-1;
    for (;ch>='0'&&ch<='9';ch=gc()) x=(x<<3)+(x<<1)+ch-'0';
    return x*f;
}
inline int read(){
    int x=0,f=1; char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar())  if (ch=='-')    f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar())    x=(x<<3)+(x<<1)+ch-'0';
    return x*f;
}
inline void print(int x){
    if (x<0)    putchar('-');
    if (x>9)    print(x/10);
    putchar(x%10+'0');
}
const int N=5e3,M=2e2;
int L[M+10][N+10],R[M+10][N+10],B[M+10][N+10],stack[N+10];
ll sum[N+10][N+10],A[N+10];
int main(){
    int n=read(),m=read(); ll Ans=0;
    for (int i=2;i<=n;i++)  A[i]=read()+A[i-1];
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
            B[j][i]=read();
    for (int i=1;i<=m;i++){
        for (int j=1,top=0;j<=n;j++){
            while (top&&B[i][stack[top]]<B[i][j])   top--;
            L[i][j]=top?stack[top]+1:1;
            stack[++top]=j;
        }
        for (int j=n,top=0;j>=1;j--){
            while (top&&B[i][stack[top]]<B[i][j])   top--;
            R[i][j]=top?stack[top]-1:n;
            stack[++top]=j;
        }
        for (int j=1;j<=n;j++){
            sum[L[i][j]][j]+=B[i][j];
            sum[L[i][j]][R[i][j]+1]-=B[i][j];
            sum[j+1][j]-=B[i][j];
            sum[j+1][R[i][j]+1]+=B[i][j];
        }
    }
    for (int i=1;i<=n;i++){
        for (int j=1;j<=n;j++)  sum[i][j]+=sum[i][j-1];
        for (int j=1;j<=n;j++)  sum[i][j]+=sum[i-1][j];
        for (int j=i;j<=n;j++)  Ans=max(Ans,sum[i][j]-A[j]+A[i]);
    }
    printf("%lld\n",Ans);
    return 0;
}