【题目】F - Yakiniku Restaurants

【题意】给定n和m,有n个饭店和m张票,给出Ai表示从饭店i到i+1的距离,给出矩阵B(i,j)表示在第i家饭店使用票j的收益,求任选起点和终点的最大(收益-代价)。n<=5000,m<=200。

【算法】单调栈+矩阵差分

【题解】直接枚举区间,很难同时计算m张票,我们反过来考虑每个B(i,j)的贡献。

对于B(i,j),令x为满足x<i,B(x,j)>B(i,j)的最大的x,令y为满足y>i,B(y,j)>B(i,j)的最小的y,则B(i,j)会对所有l∈[x+1,i]&&r∈[i,y-1]的区间贡献。

其中,x和y可以维护单调栈求得。(因为求最大,所以越早越没用)

现在将区间[x,y]视为平面上的点(x,y),那么就是矩阵加B(i,j),最后扫描每个点,这个用差分就可以了。

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

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=;
int n,m,b[maxn][maxn],s[maxn],l[maxn][maxn],r[maxn][maxn],w[maxn];
ll a[maxn],A[maxn][maxn];
int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)scanf("%lld",&a[i]),a[i]+=a[i-];
for(int i=;i<=n;i++)for(int j=;j<=m;j++)scanf("%d",&b[j][i]);
for(int i=;i<=m;i++){
int top=;
for(int j=;j<=n;j++){
while(top&&b[i][j]>s[top])top--;
if(top)l[i][j]=w[top]+;else l[i][j]=;
s[++top]=b[i][j];w[top]=j;
}
top=;
for(int j=n;j>=;j--){
while(top&&b[i][j]>s[top])top--;
if(top)r[i][j]=w[top]-;else r[i][j]=n;
s[++top]=b[i][j];w[top]=j;
}
for(int j=;j<=n;j++){
A[l[i][j]][j]+=b[i][j];
A[l[i][j]][r[i][j]+]-=b[i][j];
A[j+][j]-=b[i][j];
A[j+][r[i][j]+]+=b[i][j];
}
}
ll ans=-1ll<<;
for(int i=;i<=n;i++){
for(int j=;j<=n;j++)A[i][j]+=A[i][j-];
for(int j=;j<=n;j++)A[i][j]+=A[i-][j];
for(int j=i;j<=n;j++)ans=max(ans,A[i][j]-a[j]+a[i]);
}
printf("%lld",ans);
return ;
}

【另一解】

枚举左端点L。单看一张饭票,用单调栈预处理f[i]表示最小的x满足x>i&&B(x)>B(i),那么这张饭票的贡献其实是一个从L开始长度为k的递增位置序列c[],其中c[i]的贡献是B(c[i])-B(c[i-1])。对于所有饭票都将c[i]的贡献叠加到数组g[]上,那么处理完后g[i]表示区间包含i得到的贡献。此时就可以枚举右端点更新答案。

然后考虑左端点移动删除L,即更新为从L+1开始递增的数字。原来c[1]=L,那么实际上到c[2]就停止了,c[2]需要额外减掉B(c[2])-B(c[1])来消除上次的贡献。这个过程暴力更新,复杂度是正确的,因为只会被最近的比它大的数字引发更新,复杂度O(n^2+nm)。

这种思想是考虑左端点的移动对答案的影响,而非右端点。

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define ll long long
using namespace std;
int read(){
int s=,t=;char c;
while(!isdigit(c=getchar()))if(c=='-')t=-;
do{s=s*+c-'';}while(isdigit(c=getchar()));
return s*t;
}
const int maxn=;
int n,m,a[][maxn],f[][maxn],s[maxn],w[maxn];
ll A[maxn],g[maxn];
int main(){
n=read();m=read();
for(int i=;i<=n;i++)A[i]=read();
for(int i=;i<=n;i++)for(int j=;j<=m;j++)a[j][i]=read();
int top=;
for(int i=;i<=m;i++){
top=;
for(int j=n;j>=;j--){
while(top&&a[i][j]>=s[top])top--;
if(top)f[i][j]=w[top];else f[i][j]=n+;
s[++top]=a[i][j];w[top]=j;
}
}
for(int i=;i<=m;i++){
int j=;g[]+=a[i][];
while(f[i][j]<=n)g[f[i][j]]+=a[i][f[i][j]]-a[i][j],j=f[i][j];
}
ll ans=-1ll<<,sum=g[];ans=max(ans,sum);
for(int i=;i<=n;i++)ans=max(ans,sum=sum-A[i]+g[i]);
for(int k=;k<=n;k++){
for(int i=;i<=m;i++){
int j=k,pre=;
while(j<=n&&j!=f[i][k-])g[j]+=a[i][j]-a[i][pre],pre=j,j=f[i][j];
g[j]-=a[i][j]-a[i][k-];g[j]+=a[i][j]-a[i][pre];
}
sum=g[k];ans=max(ans,sum);
for(int j=k+;j<=n;j++)ans=max(ans,sum=sum-A[j]+g[j]);
}
printf("%lld",ans);
return ;
}

【AtCoder】ARC067 F - Yakiniku Restaurants 单调栈+矩阵差分的更多相关文章

  1. 牛客多校第三场F Planting Trees 单调栈

    Planting Trees 题意 给出一个矩阵,求最大矩阵面积满足该矩阵中任2元素的绝对值之差小于等于M T<1000) (n<500)但是题目明示单组(n*3)可过 分析 又是矩阵问题 ...

  2. AtCoder Regular Contest 067 F - Yakiniku Restaurants

    题意: 有n个餐厅排成一排,第i个与第i+1个之间距离是Ai. 有m种食物,每种食物只能在一个餐厅里吃,第j种食物在第i个餐厅里吃的收益是$b[i][j]$. 选择每种食物在哪个餐厅里吃,使收益减去走 ...

  3. AtCoder Regular Contest 063 F : Snuke’s Coloring 2 (线段树 + 单调栈)

    题意 小 \(\mathrm{C}\) 很喜欢二维染色问题,这天他拿来了一个 \(w × h\) 的二维平面 , 初始时均为白色 . 然后他在上面设置了 \(n\) 个关键点 \((X_i , Y_i ...

  4. 2019牛客暑假多校赛(第二场) F和H(单调栈)

    F-Partition problem https://ac.nowcoder.com/acm/contest/882/F 题意:输入一个数n,代表总共有2n个人,然后每个人对所有人有个贡献值,然后问 ...

  5. Looksery Cup 2015 F - Yura and Developers 单调栈+启发式合并

    F - Yura and Developers 第一次知道单调栈搞出来的区间也能启发式合并... 你把它想想成一个树的形式, 可以发现确实可以启发式合并. #include<bits/stdc+ ...

  6. AtCoder Grand Contest 005【A栈模拟,B单调栈】

    挖草,AtCoder实在是太吊了~ %%%,目前只A了两题: A题: 就是利用栈模拟一下就好了:S进栈,T的话有S就出栈,然后len减一下就好了: #include <bits/stdc++.h ...

  7. Manthan, Codefest 18 (rated, Div. 1 + Div. 2) F 单调栈 + 贡献 + 计数

    https://codeforces.com/contest/1037/problem/F 题意 function z(array a, integer k): if length(a) < k ...

  8. 2019牛客暑期多校训练营(第二场)-H Second Large Rectangle(次大子矩阵,降维,直方图+单调栈)

    题目链接:https://ac.nowcoder.com/acm/contest/882/H 题目:给n×m的由01组成的矩阵,求次大全1子矩阵的大小. 思路:第一步还是降维操作,用a[i][j]记录 ...

  9. Atcoder Regular Contest 058 D - 文字列大好きいろはちゃん / Iroha Loves Strings(单调栈+Z 函数)

    洛谷题面传送门 & Atcoder 题面传送门 神仙题. mol 一发现场(bushi)独立切掉此题的 ycx %%%%%%% 首先咱们可以想到一个非常 naive 的 DP,\(dp_{i, ...

随机推荐

  1. “四则运算生成程序——GUI支持和部分功能改进”链接

    项目成员:张金生     张政 <正文随笔发布在张金生博客中> 四则运算生成程序——GUI支持和部分功能改进

  2. 关于VS2005中C#代码用F12转到定义时,总是显示从元数据的问题

    元数据是:NET 程序集中的标记信息. 是在代码中选择了转到定义时候给定位的吧.因为没有找到源代码,VS通过反射读取元数据中的信息生成了那个. 解决方法: 1. 要把项目先添加到解决方案中. 2. 再 ...

  3. Linux上的free命令详解-收藏

    解释一下Linux上free命令的输出. 下面是free的运行结果,一共有4行.为了方便说明,我加上了列号.这样可以把free的输出看成一个二维数组FO(Free Output).例如: FO[2][ ...

  4. yiled(),wait(),sleep()方法区别

    yiled():让步 wait():等待 sleep():休眠 yiled是让步,会使当前线程由运行状态进入到就绪状态,让其他优先级高线程先执行,但是如果是同一优先级的线程,那么谁先执行就不确定了.它 ...

  5. js遍历数组和遍历对象

    可以用for in来遍历对象,具体内容如下: <script type="text/javascript">             var objs = {      ...

  6. java线程dump分析工具

    jstack和线程dump分析  java程序性能分析之thread dump和heap dump 一.[内存dump] jmap –dump:live,format=b,file=heap.bin ...

  7. C++模式学习------模板模式

    模板模式: 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中.模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤. 模板模式是一种很常用的模式,在很多的框架或者基类重载的时候都 ...

  8. 使用简单zzupdate命令轻松升级Ubuntu到较新版本

    zzupdate是一个开源的命令行实用程序,通过将几个更新命令组合到一个命令中,使得将Ubuntu桌面版和服务器版升级到更新版本的任务变得容易一些. 将Ubuntu系统升级到更新的版本并不是一项艰巨的 ...

  9. hbase batch批处理

    hbase的put(List<Put> puts),delete(List<Delete> deletes),get(List<Get> gets)都是基于batc ...

  10. BZOJ 3165: [Heoi2013]Segment

    3165: [Heoi2013]Segment Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 465  Solved: 187[Submit][Sta ...