题目传送门:https://arc067.contest.atcoder.jp/tasks/arc067_d
题目大意:
有\(N\)家烧烤店,在直线上按顺序排列,第\(i\)家烧烤店和第\(i+1\)家烧烤店的距离为\(A_i\)。你有\(M\)张烧烤券,在第\(i\)家烧烤店使用第\(j\)张券可以获得\(B_{i,j}\)的快乐,你可以在某家烧烤店使用多张券。你现在可以从某个烧烤店开始,使用所有的券,使得你的快乐值减去所走路程最大
我们考虑每个\(B_{i,j}\)的贡献,我们找到第一个一个\(B_{L,j}>B_{i,j}\)且\(L<i\),然后\(R\)类似,那么\(B_{i,j}\)对答案有贡献需要决策左端点在\((L,i]\)中,右端点在\([i,R)\)中
于是我们可以设\(f_{l,r}\)表示决策在\([l,r]\)的收益,对于每个\(B_{i,j}\),我们对\(f_{(L,i],[i,R)}\)加上\(B_{i,j}\)的贡献,可以证明,对于某张券\(j\),\(B_{1\sim n,j}\)对答案的贡献矩阵没有交集,因此我们可以用二维差分解决,最后还原\(f\)即可
/*problem from Wolfycz*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline char gc(){
static char buf[1000000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline int frd(){
int x=0,f=1; char ch=gc();
for (;ch<'0'||ch>'9';ch=gc()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=gc()) x=(x<<3)+(x<<1)+ch-'0';
return x*f;
}
inline int read(){
int x=0,f=1; char ch=getchar();
for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+ch-'0';
return x*f;
}
inline void print(int x){
if (x<0) putchar('-');
if (x>9) print(x/10);
putchar(x%10+'0');
}
const int N=5e3,M=2e2;
int L[M+10][N+10],R[M+10][N+10],B[M+10][N+10],stack[N+10];
ll sum[N+10][N+10],A[N+10];
int main(){
int n=read(),m=read(); ll Ans=0;
for (int i=2;i<=n;i++) A[i]=read()+A[i-1];
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
B[j][i]=read();
for (int i=1;i<=m;i++){
for (int j=1,top=0;j<=n;j++){
while (top&&B[i][stack[top]]<B[i][j]) top--;
L[i][j]=top?stack[top]+1:1;
stack[++top]=j;
}
for (int j=n,top=0;j>=1;j--){
while (top&&B[i][stack[top]]<B[i][j]) top--;
R[i][j]=top?stack[top]-1:n;
stack[++top]=j;
}
for (int j=1;j<=n;j++){
sum[L[i][j]][j]+=B[i][j];
sum[L[i][j]][R[i][j]+1]-=B[i][j];
sum[j+1][j]-=B[i][j];
sum[j+1][R[i][j]+1]+=B[i][j];
}
}
for (int i=1;i<=n;i++){
for (int j=1;j<=n;j++) sum[i][j]+=sum[i][j-1];
for (int j=1;j<=n;j++) sum[i][j]+=sum[i-1][j];
for (int j=i;j<=n;j++) Ans=max(Ans,sum[i][j]-A[j]+A[i]);
}
printf("%lld\n",Ans);
return 0;
}
原文地址:https://www.cnblogs.com/Wolfycz/p/10071802.html
时间: 2024-11-05 19:05:15