[arc067f]Yakiniku Restaurants

题目大意

不想讲。

做法

大概是需要知道如何给一个区间包含的所有区间打加法tag。
给[l,r]包含的所有区间+d,可以令v[l,r]+d。
最终做一遍v[l,r]+=v[l-1,r]+v[l,r+1]-v[l-1,r+1]。
容易讨论这是对的。
这题做m次,建关于最大值的笛卡尔树,那么每次相当于一个区间包含的区间中包含某个位置的可以加多少,可以转化为三个包含区间加法。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int maxn=5000+10,maxm=200+10;
ll v[maxn][maxn],d[maxn];
int b[maxm][maxn],a[maxn];
int sta[maxn],fa[maxn],lc[maxn],rc[maxn],L[maxn],R[maxn];
int i,j,k,l,t,n,m,top,root;
ll ans;
void ins(int mid,int l,int r,int x){
    v[l][r]+=(ll)x;
    v[l][mid-1]-=(ll)x;
    v[mid+1][r]-=(ll)x;
}
void dfs(int x){
    L[x]=R[x]=x;
    if (lc[x]){
        dfs(lc[x]);
        L[x]=L[lc[x]];
    }
    if (rc[x]){
        dfs(rc[x]);
        R[x]=R[rc[x]];
    }
    ins(x,L[x],R[x],a[x]);
}
void work(int id){
    int i,j;
    fo(i,1,n) a[i]=b[id][i];
    fo(i,1,n) fa[i]=lc[i]=rc[i]=0;
    top=0;
    fo(i,1,n){
        while (top&&a[i]>a[sta[top]]){
            if (fa[sta[top]]) rc[sta[top]]=i;
            rc[sta[top]]=lc[i];
            lc[i]=sta[top];
            top--;
        }
        fa[i]=sta[top];
        rc[sta[top]]=i;
        sta[++top]=i;
    }
    root=sta[1];
    dfs(root);
}
int main(){
    scanf("%d%d",&n,&m);
    fo(i,2,n){
        scanf("%d",&d[i]);
        d[i]+=d[i-1];
    }
    fo(i,1,n)
        fo(j,1,m) scanf("%d",&b[j][i]);
    fo(i,1,m) work(i);
    fo(i,1,n)
        fd(j,n,i)
            v[i][j]=v[i][j]+v[i-1][j]+v[i][j+1]-v[i-1][j+1];
    fo(i,1,n)
        fo(j,i,n)
            ans=max(ans,v[i][j]-(d[j]-d[i]));
    printf("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值