AtCoder Grand Contest 067 F - Yakiniku Restaurants
阿新 • • 發佈:2018-12-05
題目傳送門: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}\)
於是我們可以設\(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; }