1. 程式人生 > >NOIP模擬賽:pks玩爐石 子矩陣中位數 Two-Pointer

NOIP模擬賽:pks玩爐石 子矩陣中位數 Two-Pointer

NOIP模擬賽:pks玩爐石

題面描述

在這裡插入圖片描述 在這裡插入圖片描述

分析

首先考慮下面這個問題: 給定若干個數,找到某個數使得所有數與其差的絕對值的和最小。 顯然,這個數只能是中位數(偶數的話兩個,奇數一個)。 現在問題轉化為,給定一個矩陣,求中位數大於某個數的最大子矩陣。 再考慮這樣一個問題,給定一個序列aa,判斷其中位數是否大於某個數xx。 也就是說,Cnt(a[i]x)Cnt(a[i]<x)Cnt(a[i]\ge x)\ge Cnt(a[i] < x) 於是可以將大於等於HH的數看成11,小於的看成1-1,原問題轉化為找到子矩陣和大於等於0

0的最大子矩陣。 列舉上下界,問題轉化為最長的和為正數的子段。 考慮字首和,問題轉化為找最遠正序對,也就是MaxsisjjiMax_{s_i\le s_j}j-i 如果原序列有序,顯然可以TwopointerTwo-pointer掃。 對於ii,若i<k,siski<k,s_i\le s_k,那麼顯然kk沒用。於是可以將ii化成一個遞減的序列(從頭到尾掃) 對於jj,若j>k,sjskj>k,s_j \ge s_k,那麼顯然k
k
沒用。於是也可以將jj化成一個遞減的序列(從尾到頭掃) 於是本題可以在O(n3)O(n^3)內解決。

程式碼

#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; }