【AtCoder Regular Contest 067 F】Yakiniku Restaurants

二维差分,单调队列。

题目大意:

数轴上有 $n$ 家店,每家店都有 $1\sim m$ 这 $m$ 种食品,$w_{i,j}$ 表示第 $i$ 家店第 $j$ 种食品的美味度。

你要吃到 $1\sim m$ 的所有食品,并且每种食品只能吃一次。

从一家店走到另一家店,每走一个单位,快乐度降低 $1$。吃美味度为 $W$ 的食品,快乐度也增加 $W$。

最大化最后的快乐度。

题解:

由于是在数轴上,所以肯定是从一家店一直往右吃,到另外一家店停下。

我们考虑对每一对 $l,r$,求出只吃 $l\sim r$ 店的食品的最大快乐度。

我们对每一种食品分别考虑。对于一种食品而言,在 $l\sim r$ 中,肯定选美味度最大的吃。

我们考虑对每一家店的食品 $i$ 求出 $L_i,R_i$ 表示左端点在 $[L_i,i]$,右端点在 $[i,R_i]$ 中时,$i$ 的快乐度都是最大的。且 $[L_i,R_i]$ 是满足条件的极大区间。这可以使用单调队列在 $O(n)$ 内解决。

由于存在快乐度相等,在求的时候可以规定,向左碰到相等时继续,向右碰到相等时停止。

这样,一个食品 $i$ 就会影响到左端点在 $[L_i,i]$,右端点在 $[i,R_i]$ 的矩形区域,而同一种食品的所有矩形是互不相交的。所以可以用差分做矩形加。

最后再 $O(n^2)$ 求出矩形里的所有值,取减去两点距离后的最大方案即可。

时间复杂度 $O(n^2+nm)$。

Code:

#include<cstdio>
#include<algorithm>
const int N=5005;
typedef long long LL;
int n,m;
LL s[N][N],ans,A[N];
int L[N],R[N],sta[N],top;
struct data{
    int B[N];
    void work(){
        L[1]=1,R[n]=n,sta[top=1]=1,sta[0]=0;
        for(int i=2;i<=n;++i){
            while(top&&B[sta[top]]<=B[i])--top;
            L[i]=sta[top]+1;
            sta[++top]=i;
        }
        sta[top=1]=n,sta[0]=n+1;
        for(int i=n-1;i;--i){
            while(top&&B[sta[top]]<B[i])--top;
            R[i]=sta[top]-1;
            sta[++top]=i;
        }
        for(int i=1;i<=n;++i){
            int r1=L[i],c1=i,r2=i,c2=R[i];
            s[r1][c1]+=B[i],s[r1][c2+1]-=B[i];
            s[r2+1][c1]-=B[i],s[r2+1][c2+1]+=B[i];
        }
    }
}d[202];
int main(){
    scanf("%d%d",&n,&m);
    for(int i=2;i<=n;++i)scanf("%lld",A+i),A[i]+=A[i-1];
    for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j)scanf("%d",&d[j].B[i]);
    for(int i=1;i<=m;++i)d[i].work();
    for(int i=1;i<=n;++i)
    for(int j=1;j<=n;++j){
        s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
        if(i<=j&&s[i][j]-A[j]+A[i]>ans)ans=s[i][j]-A[j]+A[i];
    }
    printf("%lld\n",ans);
    return 0;
}