NOIP模擬賽:pks玩爐石 子矩陣中位數 Two-Pointer
阿新 • • 發佈:2018-12-19
NOIP模擬賽:pks玩爐石
題面描述
分析
首先考慮下面這個問題: 給定若干個數,找到某個數使得所有數與其差的絕對值的和最小。 顯然,這個數只能是中位數(偶數的話兩個,奇數一個)。 現在問題轉化為,給定一個矩陣,求中位數大於某個數的最大子矩陣。 再考慮這樣一個問題,給定一個序列,判斷其中位數是否大於某個數。 也就是說, 於是可以將大於等於的數看成,小於的看成,原問題轉化為找到子矩陣和大於等於的最大子矩陣。 列舉上下界,問題轉化為最長的和為正數的子段。 考慮字首和,問題轉化為找最遠正序對,也就是 如果原序列有序,顯然可以掃。 對於,若,那麼顯然沒用。於是可以將化成一個遞減的序列(從頭到尾掃) 對於,若,那麼顯然沒用。於是也可以將化成一個遞減的序列(從尾到頭掃) 於是本題可以在內解決。
程式碼
#include<bits/stdc++.h>
int ri() {
char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
const int N = 255;
int a[N][N], b[N][N], h[N], g[N], c[N], n, m;
int Work() {
for(int i = 1;i <= m; ++i) c[i] += c[i - 1];
int r = 0;
h[h[0] = 1] = 0;
for(int i = 1;i <= m; ++i)
if(c[i] < c[h[h[0]]]) h[++h[0]] = i;
g[g[0] = 1] = m;
for(int i = m; ~i; --i)
if(c[i] > c[g[g[0]]]) g[++g[0]] = i;
for(int i = 1, j = g[0];i <= h[0]; ++i) {
for(;j != 1 && c[h[i]] <= c[g[j - 1]];) --j;
r = std::max(r, g[j] - h[i]);
}
return r;
}
int Que() {
int A = 0;
for(int l = 1;l <= n; ++l)
for(int r = l; r <= n; ++r) {
for(int i = 1;i <= m; ++i) c[i] = b[r][i] - b[l - 1][i];
A = std::max(A, Work() * (r - l + 1));
}
return A;
}
int main() {
freopen("hearthstone.in","r",stdin);
freopen("hearthstone.out","w",stdout);
int T = ri();
for(int C = 1;C <= T; ++C) {
printf("Case %d:\n", C);
n = ri(); m = ri();
for(int i = 1;i <= n; ++i)
for(int j = 1;j <= m; ++j)
a[i][j] = ri();
for(int Q = ri();Q--;) {
int h = ri();
for(int i = 1;i <= n; ++i)
for(int j = 1;j <= m; ++j)
b[i][j] = b[i - 1][j] + (a[i][j] >= h ? 1 : -1);
printf("%d\n", Que());
}
}
return 0;
}