[ABC311G] One More Grid Task
題目資訊
題面翻譯
給你一個 \(n\times m\) 的矩陣 \(a\),求:
題目描述
$ N\ \times\ M $ のグリッドがあり、上から $ i $ 行目、左から $ j $ 列目のマス $ (i,j) $ には非負整數 $ A_{i,j} $ が書かれています。
このグリッドのうち長方領域をひとつ選び、それを $ R $ とします。
厳密には、長方領域は以下の手順で選ばれます。
- $ 1\ \le\ l_x\ \le\ r_x\ \le\ N,\ 1\ \le\ l_y\ \le\ r_y\ \le\ M $ なる整數 $ l_x,\ r_x,\ l_y,\ r_y $ を選ぶ。
- このとき、整數 $ i,j $ が $ l_x\ \le\ i\ \le\ r_x $ かつ $ l_y\ \le\ j\ \le\ r_y $ を満たす、またその時に限って、マス $ (i,j) $ は $ R $ に含まれる。
適切に $ R $ を選ぶことによって、 $ f(R)\ = $ ( $ R $ 內のマスに書かれた整數の総和 ) $ \times $ ( $ R $ 內のマスに書かれた整數の最小値 ) として達成可能な最大値を求めてください。
輸入格式
入力は以下の形式で標準入力から與えられる。
$ N $ $ M $ $ A_{1,1} $ $ A_{1,2} $ $ \dots $ $ A_{1,M} $ $ A_{2,1} $ $ A_{2,2} $ $ \dots $ $ A_{2,M} $ $ \vdots $ $ A_{N,1} $ $ A_{N,2} $ $ \dots $ $ A_{N,M} $
輸出格式
答えを整數として出力せよ。
樣例 #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
提示
制約
- 入力は全て整數
- $ 1\ \le\ N,M\ \le\ 300 $
- $ 1\ \le\ A_{i,j}\ \le\ 300 $
Sample Explanation 1
左上がマス $ (1,1) $ 、右下がマス $ (2,2) $ の長方領域を選ぶことで、 $ f(R)\ =\ (5+4+4+3)\ \times\ \min(5,4,4,3)\ =\ 48 $ が達成でき、これが達成可能な最大値です。
題目思路
演算法一
我們可以考慮直接列舉左上角,列舉右下角,暴力求和以及暴力求最小值。
時間複雜度:\(O(n^3m^3)\).
演算法二
演算法一顯然不行。我們可以提前預處理,用上資料結構(比如說樹狀陣列、線段樹樹),直接求最小值和和。
時間複雜度:\(O(n^2m^2\log n\log m)\)。
演算法三
演算法二顯然也不行。考慮最佳化。
我們知道:任何矩陣的問題都可以壓縮成一維問題。即列舉上屆、列舉下屆,求方案數。
預處理後,我們可以記錄 \(left_i\)、\(right_i\) 分別代表能延伸的最左邊,能延伸的最右邊。
直接使用單調棧維護即可。
時間複雜度:\(O(n^2m\log n)\)。
程式碼
// 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
、題解
單調棧
、線段樹
、樹狀陣列
、資料結構