[ABC311G] One More Grid Task
[ABC311G] One More Grid Task
题目信息
题面翻译
给你一个 的矩阵 ,求:
题目描述
のグリッドがあり、上から 行目、左から 列目のマス には非負整数 が書かれています。
このグリッドのうち長方領域をひとつ選び、それを とします。
厳密には、長方領域は以下の手順で選ばれます。
- なる整数 を選ぶ。
- このとき、整数 が かつ を満たす、またその時に限って、マス は に含まれる。
適切に を選ぶことによって、 ( 内のマスに書かれた整数の総和 ) ( 内のマスに書かれた整数の最小値 ) として達成可能な最大値を求めてください。
输入格式
入力は以下の形式で標準入力から与えられる。
输出格式
答えを整数として出力せよ。
样例 #1
样例输入 #1
3 3 5 4 3 4 3 2 3 2 1
样例输出 #1
48
样例 #2
样例输入 #2
4 5 3 1 4 1 5 9 2 6 5 3 5 8 9 7 9 3 2 3 8 4
样例输出 #2
231
样例 #3
样例输入 #3
6 6 1 300 300 300 300 300 300 1 300 300 300 300 300 300 1 300 300 300 300 300 300 1 300 300 300 300 300 300 1 300 300 300 300 300 300 1
样例输出 #3
810000
提示
制約
- 入力は全て整数
Sample Explanation 1
左上がマス 、右下がマス の長方領域を選ぶことで、 が達成でき、これが達成可能な最大値です。
题目思路
算法一
我们可以考虑直接枚举左上角,枚举右下角,暴力求和以及暴力求最小值。
时间复杂度:.
算法二
算法一显然不行。我们可以提前预处理,用上数据结构(比如说树状数组、线段树树),直接求最小值和和。
时间复杂度:。
算法三
算法二显然也不行。考虑优化。
我们知道:任何矩阵的问题都可以压缩成一维问题。即枚举上界、枚举下界,求方案数。
预处理后,我们可以记录 、 分别代表能延伸的最左边,能延伸的最右边。
直接使用单调栈维护即可。
时间复杂度:。
代码
// LUOGU_RID: 161862093 #include<bits/stdc++.h> #define int long long #define left _zqh_ #define right flying_hq using namespace std; const int MAXN = 320; const int LOGN = log2(MAXN)+5; int n,m,matrix[MAXN][MAXN],ans; void read(int &x){ x = 0;int p = 1;char ch; do{ ch = getchar(); if(ch=='-') p = -1; }while(!isdigit(ch)); while(isdigit(ch)){ x*=10; x+=ch-'0'; ch = getchar(); } x*=p; } int sum[MAXN][MAXN],now[MAXN],left[MAXN],right[MAXN],q[MAXN],minn[MAXN]; class Segmenttree{ private: struct segment{ int l,r,min=0X3F3F3F3F; }tree[MAXN<<3]; void pushup(int id){ tree[id].min = min(tree[id*2].min,tree[id*2+1].min); } public: void build(int k,int l,int r){ tree[k].l = l; tree[k].r = r; if(l==r) return; int mid = (l+r)>>1; build(k*2,l,mid); build(k*2+1,mid+1,r); } void change(int k,int p,int q){ if(tree[k].l>p||tree[k].r<p) return; if(tree[k].l>=p&&tree[k].r<=p){ tree[k].min = q; return; } change(k*2,p,q); change(k*2+1,p,q); pushup(k); } int getmin(int k,int l,int r){ if(tree[k].l>r||tree[k].r<l) return 0x3f3f3f3f; if(tree[k].l>=l&&tree[k].r<=r){ return tree[k].min; } return min(getmin(k*2,l,r),getmin(k*2+1,l,r)); } }tree[MAXN]; signed main(){ read(n);read(m); for(int i = 1;i<=m;i++) tree[i].build(1,1,n); for(int i = 1;i<=n;i++){ for(int j = 1;j<=m;j++){ read(matrix[i][j]); sum[i][j] = sum[i-1][j]+matrix[i][j]; tree[j].change(1,i,matrix[i][j]); } } for(int i = 1;i<=n;i++){ for(int j = i;j<=n;j++){ if(i == 1&&j== 2){ int k = 0; } for(int k = 1;k<=m;k++){ now[k] = sum[j][k]-sum[i-1][k]; q[k] = q[k-1]+now[k]; minn[k] = tree[k].getmin(1,i,j); } stack<pair<int,int>> st; for(int k = 1;k<=m;k++){ while(!st.empty()&&st.top().second>=minn[k]) st.pop(); if(st.empty()){ left[k] = 1; }else{ left[k] = st.top().first+1; } st.push({k,minn[k]}); } while(!st.empty()){ st.pop(); } for(int k = m;k>=1;k--){ while(!st.empty()&&st.top().second>=minn[k]) st.pop(); if(st.empty()){ right[k] = m; }else{ right[k] = st.top().first-1; } st.push({k,minn[k]}); } for(int k = 1;k<=m;k++){ ans = max(ans,(q[right[k]]-q[left[k]-1])*minn[k]); } } } printf("%lld",ans); return 0; }
tag
Atcoder
、题解
单调栈
、线段树
、树状数组
、数据结构
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】