[Atcoder 2289] Yakiniku Restaurants

题目

ssq偷偷在马路上开了N家烧烤店,并带着魔法少女伊莉雅来吃。

烧烤店编号依次是1到N,第i家烧烤店和第i+1家烧烤店的距离是Ai。

由于他其实是老板,所以他有M张餐票,如果在第i家烧烤店使用第j张餐票装十三就会获得魔法少女伊莉雅Bi,j的好感度(可以用很多张,但是每张只能用一次)。

但是魔法少女伊莉雅非常不喜欢走路,因此他要使得从魔法少女伊莉雅那里获得的好感度之和,减去魔法少女伊莉雅走的总距离得到的结果最大。

问最大结果是多大。

伊莉雅的起点可以任意选择。

2≤N≤5000

1≤M≤200

1≤Ai, Bi,j≤10^9

题解

餐票不需要按顺序用,所以不能按照m的顺序dp

注意在最好的情况下,肯定不会走重复的路。

考虑指定一段区间,然后累计这段区间饭票的最大值就行了,但是是$O(n^2*m)$

但其实很多位置并不会改变最大值。

考虑固定左端点,右端点往右移动的过程中,我们可以预先计算出每一步所带来的增量,具体而言,就是如果这一步相对于左边贡献了新的最大值,那么增量就是新最大值-旧最大值,否则为0,把m个都累计在一起,这样就不用每一右端点都遍历m遍。

现在考虑如何在左端点移动的过程维护该增量数组

让左端点从右往左移动

如果有新的最大值,那么它可以把后面的全部顶掉

如何不是最大,也留待查看,因为它离左端点更近。

这样最大值从左往右上升

用单调队列来维护即可

注意队列去掉和新加入元素时要更新对应位置的增量数组。

增加时在它的位置的增量加Bi,j,在右边第一个比它大的位置的增量减去Bi,j,(这时区间最大值不是它了,要消除影响)

删除时反过来即可

大体思路:暴力算法-用辅助数组加速-用数据结构维护辅助数组

 代码

#include<bits/stdc++.h>
using namespace std;
#define N 5010
#define int long long
int cost[N],arr[N][210],maxn[N],q[210][N],len[N],val[N];
signed main()
{
	int n,m;
	cin>>n>>m;
	for(int i=1;i<n;i++)
	{
		scanf("%lld",&cost[i]);
		cost[i]+=cost[i-1];
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++) scanf("%lld",&arr[i][j]);
	}
	int ans=0;
	for(int i=n;i;i--)
	{
		for(int j=1;j<=m;j++)
		{
			while(len[j])
			{
				int t=q[j][len[j]];
				if(arr[t][j]>arr[i][j]) break;
				val[t]-=arr[t][j];
				if(len[j]>1) val[q[j][len[j]-1]]+=arr[t][j];
				len[j]--;
			}
			q[j][++len[j]]=i;
			val[i]+=arr[i][j];
			if(len[j]>1) val[q[j][len[j]-1]]-=arr[i][j];
		}
		int tot=0;
		for(int j=i;j<=n;j++)
		{
			int dis=cost[j-1]-cost[i-1];
			tot+=val[j];
			ans=max(ans,tot-dis);
		}
	}
	cout<<ans;
}

  

posted @ 2021-08-09 09:35  linzhuohang  阅读(33)  评论(0编辑  收藏  举报