[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; }
看都看了,顺手点个推荐呗 :)