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;
}

猜你喜欢

转载自www.cnblogs.com/Wolfycz/p/10071802.html