题意
就是给你一个矩阵只含有0或者1,让你找一个第二大的内部元素全部为1的子矩形
题解
考虑枚举所有的最大矩形,删去一行或者一列构成当前矩形的第二大,记录所有这些信息,排序就行了,注意同一个矩形不要算多次,所以我这里直接重载矩形结构体的比较函数
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn= 1005 ;
struct polygon{
int area, lx, ly, rx, ry;
polygon ( int a= 0 , int b= 0 , int c= 0 , int d= 0 , int e= 0 ) {
area= a; lx= b; ly= c; rx= d; ry= e;
}
friend bool operator< ( const polygon& p1, const polygon& p2) {
if ( p1. area!= p2. area) return p1. area< p2. area;
if ( p1. lx!= p2. lx) return p1. lx< p2. lx;
if ( p1. ly!= p2. ly) return p1. ly< p2. ly;
if ( p1. rx!= p2. rx) return p1. rx< p2. rx;
return p1. ry< p2. ry;
}
friend bool operator== ( const polygon & p1, const polygon & p2) {
return p1. area== p2. area&& p1. lx== p2. lx&& p1. ly== p2. ly&& p1. rx== p2. rx&& p1. ry== p2. ry;
}
} p[ 3 * maxn* maxn] ;
int cnt= 0 ;
int n, m, dp[ maxn] [ maxn] ;
char s[ maxn] [ maxn] ;
int a[ maxn] , sta[ maxn] , tot, minl[ maxn] , minr[ maxn] ;
void solve_min_l ( int k) {
tot= 0 ;
for ( int i= m; i>= 1 ; i-- ) {
while ( tot&& dp[ k] [ i] < dp[ k] [ sta[ tot] ] ) { minl[ sta[ tot] ] = i; tot-- ; }
sta[ ++ tot] = i;
}
while ( tot) { minl[ sta[ tot] ] = 0 ; tot-- ; }
}
void solve_min_r ( int k) {
tot= 0 ;
for ( int i= 1 ; i<= m; i++ ) {
while ( tot&& dp[ k] [ i] < dp[ k] [ sta[ tot] ] ) { minr[ sta[ tot] ] = i; tot-- ; }
sta[ ++ tot] = i;
}
while ( tot) { minr[ sta[ tot] ] = m+ 1 ; tot-- ; }
}
int main ( )
{
scanf ( "%d %d" , & n, & m) ;
for ( int i= 1 ; i<= n; i++ ) scanf ( "%s" , s[ i] + 1 ) ;
for ( int i= n; i>= 1 ; i-- ) for ( int j= 1 ; j<= m; j++ ) dp[ i] [ j] = ( s[ i] [ j] == '1' ? dp[ i+ 1 ] [ j] + 1 : 0 ) ;
for ( int i= 1 ; i<= n; i++ ) {
solve_min_l ( i) ; solve_min_r ( i) ;
for ( int j= 1 ; j<= m; j++ ) if ( dp[ i] [ j] ) {
p[ ++ cnt] = polygon ( dp[ i] [ j] * ( minr[ j] - minl[ j] - 1 ) , i, minl[ j] + 1 , i+ dp[ i] [ j] - 1 , minr[ j] - 1 ) ;
if ( minr[ j] - minl[ j] - 2 > 0 ) p[ ++ cnt] = polygon ( dp[ i] [ j] * ( minr[ j] - minl[ j] - 2 ) , i, minl[ j] + 1 , i+ dp[ i] [ j] - 1 , minr[ j] - 2 ) ;
if ( dp[ i] [ j] > 1 ) p[ ++ cnt] = polygon ( ( dp[ i] [ j] - 1 ) * ( minr[ j] - minl[ j] - 1 ) , i, minl[ j] + 1 , i+ dp[ i] [ j] - 2 , minr[ j] - 1 ) ;
}
}
sort ( p+ 1 , p+ cnt+ 1 ) ;
if ( ! cnt) return printf ( "0\n" ) , 0 ;
int maxx= 0 ; int memor= cnt- 1 ;
while ( memor&& p[ memor] == p[ cnt] ) memor-- ;
if ( memor) maxx= p[ memor] . area;
printf ( "%d\n" , maxx) ;
}
题意
就是给你一个
n
×
n
n\times n
n × n 的矩阵,让你寻找一个最大的子矩阵使得这个矩阵的最大值与最小值相减小于等于
m
m
m
题解
考虑枚举矩形的上下边界,然后考虑能不能
O
(
n
)
O(n)
O ( n ) 处理出最大的矩形,可以用两个单调队列分别维护区间最大最小值,当然最大值对应一个递减队列,最小值对应一个递增队列,每次插入当前这一列的最大值,最小值后,首先让维护好两个队列的单调性,然后如果递减队列的头元素减去递增队列的头元素大于
m
m
m ,显然需要
p
o
p
pop
p o p 头元素,那么
p
o
p
pop
p o p 哪一个队列呢?可以比较一下两个队列的首元素位置大小关系,因为当前差值太大,你可以将最大值变小【
p
o
p
pop
p o p 递减队列】也可以将最小值变大【
p
o
p
pop
p o p 递增队列】,从贪心的角度,你应该选择头元素位置小的那个队列
p
o
p
pop
p o p ,那么怎么知道矩形的当前可行宽度呢?可以维护一个指针
p
o
s
pos
p o s ,表示当前可行的最左边界,每次
p
o
p
pop
p o p 两个队列中的任意一个队列时更新可行左边界
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn= 505 ;
#define inf 0x3f3f3f3f
namespace IO{
#define BUF_SIZE 100000
#define OUT_SIZE 100000
bool IOerror= 0 ;
inline char nc ( ) {
static char buf[ BUF_SIZE] , * p1= buf+ BUF_SIZE, * pend= buf+ BUF_SIZE;
if ( p1== pend) {
p1= buf; pend= buf+ fread ( buf, 1 , BUF_SIZE, stdin ) ;
if ( pend== p1) { IOerror= 1 ; return - 1 ; }
}
return * p1++ ;
}
inline bool blank ( char ch) { return ch== ' ' || ch== '\n' || ch== '\r' || ch== '\t' ; }
template< typename T>
inline bool read ( T & x) {
bool sign= 0 ; char ch= nc ( ) ; x= 0 ;
for ( ; blank ( ch) ; ch= nc ( ) ) ;
if ( IOerror) return false;
if ( ch== '-' ) sign= 1 , ch= nc ( ) ;
for ( ; ch>= '0' && ch<= '9' ; ch= nc ( ) ) x= x* 10 + ch- '0' ;
if ( sign) x= - x; return true;
}
} ;
using namespace IO;
int a[ maxn] [ maxn] , maxx[ maxn] , minn[ maxn] , que[ 2 ] [ maxn] , head[ 2 ] , tail[ 2 ] , n, m;
void init_que ( )
{
head[ 0 ] = head[ 1 ] = 1 ;
tail[ 0 ] = tail[ 1 ] = 0 ;
}
int main ( )
{
int t; read ( t) ;
while ( t-- ) {
read ( n) ; read ( m) ; int ans= 0 ;
for ( int i= 1 ; i<= n; i++ ) for ( int j= 1 ; j<= n; j++ ) read ( a[ i] [ j] ) ;
for ( int i= 1 ; i<= n; i++ ) {
for ( int j= 1 ; j<= n; j++ ) minn[ j] = inf, maxx[ j] = - inf;
for ( int j= i; j<= n; j++ ) {
init_que ( ) ;
for ( int k= 1 , pos= 0 ; k<= n; k++ ) {
minn[ k] = min ( minn[ k] , a[ j] [ k] ) , maxx[ k] = max ( maxx[ k] , a[ j] [ k] ) ;
while ( head[ 0 ] <= tail[ 0 ] && maxx[ k] >= maxx[ que[ 0 ] [ tail[ 0 ] ] ] ) tail[ 0 ] -- ;
while ( head[ 1 ] <= tail[ 1 ] && minn[ k] <= minn[ que[ 1 ] [ tail[ 1 ] ] ] ) tail[ 1 ] -- ;
que[ 0 ] [ ++ tail[ 0 ] ] = k; que[ 1 ] [ ++ tail[ 1 ] ] = k;
while ( head[ 0 ] <= tail[ 0 ] && head[ 1 ] <= tail[ 1 ] && maxx[ que[ 0 ] [ head[ 0 ] ] ] - minn[ que[ 1 ] [ head[ 1 ] ] ] > m) {
if ( que[ 0 ] [ head[ 0 ] ] > que[ 1 ] [ head[ 1 ] ] ) {
if ( head[ 1 ] < tail[ 1 ] ) pos= que[ 1 ] [ head[ 1 ] ++ ] ;
else head[ 1 ] ++ , pos= k;
} else {
if ( head[ 0 ] < tail[ 0 ] ) pos= que[ 0 ] [ head[ 0 ] ++ ] ;
else head[ 0 ] ++ , pos= k;
}
}
ans= max ( ans, ( j- i+ 1 ) * ( k- pos) ) ;
}
}
}
printf ( "%d\n" , ans) ;
}
}
题意
题解
由于涉及区间最小数,考虑枚举每一个数作为区间最小值时的最大值,显然这个数作为某个区间的最小值的话这个区间是有限制的,所以考虑单调栈求出每一个点对应的合法区间,然后线段树维护【这题不能
s
t
st
s t 表,空间会爆】数组
b
b
b 的前缀和的区间最大值以及最小值,如果枚举的最小值为负,由于要乘起来最大,所以可以把当前节点右边合法的最小值减去左边最大值,如果为正则相反 这题还可以建数组
a
a
a 的笛卡尔树,每个节点保存这个节点的子树的所有b的前缀和
s
u
m
sum
s u m 最大,最小值,然后做一次dfs,讨论根节点
a
a
a 值正负,如果为正,将右子树和根节点的
s
u
m
sum
s u m 最大值减左子树最小值再乘以根节点
a
a
a 值,如果为负,反过来就好了
单调栈+线段树代码(O(n log n))
#include <bits/stdc++.h>
using namespace std;
const int maxn= 3e6 + 10 ;
namespace IO{
#define BUF_SIZE 100000
#define OUT_SIZE 100000
bool IOerror= 0 ;
inline char nc ( ) {
static char buf[ BUF_SIZE] , * p1= buf+ BUF_SIZE, * pend= buf+ BUF_SIZE;
if ( p1== pend) {
p1= buf; pend= buf+ fread ( buf, 1 , BUF_SIZE, stdin ) ;
if ( pend== p1) { IOerror= 1 ; return - 1 ; }
}
return * p1++ ;
}
inline bool blank ( char ch) { return ch== ' ' || ch== '\n' || ch== '\r' || ch== '\t' ; }
template< typename T>
inline bool read ( T & x) {
bool sign= 0 ; char ch= nc ( ) ; x= 0 ;
for ( ; blank ( ch) ; ch= nc ( ) ) ;
if ( IOerror) return false;
if ( ch== '-' ) sign= 1 , ch= nc ( ) ;
for ( ; ch>= '0' && ch<= '9' ; ch= nc ( ) ) x= x* 10 + ch- '0' ;
if ( sign) x= - x; return true;
}
} ;
using namespace IO;
int n, a[ maxn] , b[ maxn] , sta[ maxn] [ 2 ] , tot, minl[ maxn] , minr[ maxn] ;
long long sum[ maxn] ;
void solve_min_l ( ) {
tot= 0 ;
for ( int i= n; i>= 1 ; i-- ) {
while ( tot&& a[ i] < sta[ tot] [ 0 ] ) { minl[ sta[ tot] [ 1 ] ] = i; tot-- ; }
sta[ ++ tot] [ 0 ] = a[ i] ; sta[ tot] [ 1 ] = i;
}
while ( tot) { minl[ sta[ tot] [ 1 ] ] = 0 ; tot-- ; }
}
void solve_min_r ( ) {
tot= 0 ;
for ( int i= 1 ; i<= n; i++ ) {
while ( tot&& a[ i] < sta[ tot] [ 0 ] ) { minr[ sta[ tot] [ 1 ] ] = i; tot-- ; }
sta[ ++ tot] [ 0 ] = a[ i] ; sta[ tot] [ 1 ] = i;
}
while ( tot) { minr[ sta[ tot] [ 1 ] ] = n+ 1 ; tot-- ; }
}
long long mark[ maxn<< 2 ] , minn[ maxn<< 2 ] , maxx[ maxn<< 2 ] ;
void pushup ( int id)
{
minn[ id] = min ( minn[ id<< 1 ] , minn[ id<< 1 | 1 ] ) ;
maxx[ id] = max ( maxx[ id<< 1 ] , maxx[ id<< 1 | 1 ] ) ;
}
void down ( int id)
{
mark[ id<< 1 ] + = mark[ id] ; mark[ id<< 1 | 1 ] + = mark[ id] ;
minn[ id<< 1 ] + = mark[ id] ; minn[ id<< 1 | 1 ] + = mark[ id] ;
maxx[ id<< 1 ] + = mark[ id] ; maxx[ id<< 1 | 1 ] + = mark[ id] ;
mark[ id] = 0 ;
}
void build ( int id, int l, int r)
{
mark[ id] = minn[ id] = maxx[ id] = 0 ;
if ( l== r) { minn[ id] = maxx[ id] = sum[ l] ; return ; }
int mid= ( l+ r) >> 1 ;
build ( id<< 1 , l, mid) ; build ( id<< 1 | 1 , mid+ 1 , r) ;
pushup ( id) ;
}
void update ( int id, int L, int R, int l, int r, long long add)
{
if ( l<= L&& R<= r) {
mark[ id] + = add; minn[ id] + = add; maxx[ id] + = add;
return ;
}
if ( mark[ id] != 0 ) down ( id) ; int mid= ( L+ R) >> 1 ;
if ( l<= mid) update ( id<< 1 , L, mid, l, r, add) ;
if ( r> mid) update ( id<< 1 | 1 , mid+ 1 , R, l, r, add) ;
pushup ( id) ;
}
long long query_max ( int id, int L, int R, int l, int r)
{
if ( l<= L&& R<= r) return maxx[ id] ; long long res= - 0x3f3f3f3f3f3f3f3f ;
if ( mark[ id] != 0 ) down ( id) ; int mid= ( L+ R) >> 1 ;
if ( l<= mid) res= max ( res, query_max ( id<< 1 , L, mid, l, r) ) ;
if ( r> mid) res= max ( res, query_max ( id<< 1 | 1 , mid+ 1 , R, l, r) ) ;
return res;
}
long long query_min ( int id, int L, int R, int l, int r)
{
if ( l<= L&& R<= r) return minn[ id] ; long long res= 0x3f3f3f3f3f3f3f3f ;
if ( mark[ id] != 0 ) down ( id) ; int mid= ( L+ R) >> 1 ;
if ( l<= mid) res= min ( res, query_min ( id<< 1 , L, mid, l, r) ) ;
if ( r> mid) res= min ( res, query_min ( id<< 1 | 1 , mid+ 1 , R, l, r) ) ;
return res;
}
int main ( )
{
read ( n) ;
for ( int i= 1 ; i<= n; i++ ) read ( a[ i] ) ;
for ( int i= 1 ; i<= n; i++ ) read ( b[ i] ) , sum[ i] = sum[ i- 1 ] + b[ i] ;
solve_min_l ( ) ; solve_min_r ( ) ; build ( 1 , 0 , n) ;
long long ans= - 0x3f3f3f3f3f3f3f3f ;
for ( int i= 1 ; i<= n; i++ ) {
if ( a[ i] > 0 ) ans= max ( ans, a[ i] * ( query_max ( 1 , 0 , n, i, minr[ i] - 1 ) - query_min ( 1 , 0 , n, minl[ i] , i- 1 ) ) ) ;
else ans= max ( ans, a[ i] * ( query_min ( 1 , 0 , n, i, minr[ i] - 1 ) - query_max ( 1 , 0 , n, minl[ i] , i- 1 ) ) ) ;
}
printf ( "%lld\n" , ans) ;
}
笛卡尔树代码(O(n))
#include <bits/stdc++.h>
using namespace std;
const int maxn= 3e6 + 10 ;
#define inf 0x3f3f3f3f3f3f3f3f
int n, a[ maxn] , b[ maxn] ; long long sum[ maxn] ;
namespace IO{
#define BUF_SIZE 100000
#define OUT_SIZE 100000
bool IOerror= 0 ;
inline char nc ( ) {
static char buf[ BUF_SIZE] , * p1= buf+ BUF_SIZE, * pend= buf+ BUF_SIZE;
if ( p1== pend) {
p1= buf; pend= buf+ fread ( buf, 1 , BUF_SIZE, stdin ) ;
if ( pend== p1) { IOerror= 1 ; return - 1 ; }
}
return * p1++ ;
}
inline bool blank ( char ch) { return ch== ' ' || ch== '\n' || ch== '\r' || ch== '\t' ; }
template< typename T>
inline bool read ( T & x) {
bool sign= 0 ; char ch= nc ( ) ; x= 0 ;
for ( ; blank ( ch) ; ch= nc ( ) ) ;
if ( IOerror) return false;
if ( ch== '-' ) sign= 1 , ch= nc ( ) ;
for ( ; ch>= '0' && ch<= '9' ; ch= nc ( ) ) x= x* 10 + ch- '0' ;
if ( sign) x= - x; return true;
}
} ;
using namespace IO;
struct dical_tree{
int tot, sta[ maxn] , root;
struct node{
int val;
int fa, ls, rs;
long long maxx, minn;
node ( int v= 0 , long long a= - inf, long long b= inf, int f= 0 , int l= 0 , int r= 0 ) {
val= v; maxx= a, minn= b, fa= f; ls= l; rs= r;
}
} tree[ maxn] ;
void build ( int a[ ] , int n) {
tot= root= 1 ;
sta[ 1 ] = 1 ; tree[ 1 ] = node ( a[ 1 ] , sum[ 1 ] , sum[ 1 ] , 0 , 0 , 0 ) ;
for ( int i= 2 ; i<= n; i++ ) {
while ( tot&& a[ i] < tree[ sta[ tot] ] . val) tot-- ;
if ( tot) {
tree[ i] = node ( a[ i] , sum[ i] , sum[ i] , sta[ tot] , tree[ sta[ tot] ] . rs, 0 ) ;
tree[ tree[ sta[ tot] ] . rs] . fa= i;
tree[ sta[ tot] ] . rs= i;
} else {
tree[ i] = node ( a[ i] , sum[ i] , sum[ i] , 0 , root, 0 ) ;
tree[ root] . fa= i;
root= i;
}
sta[ ++ tot] = i;
}
}
void pushup ( int cur) {
tree[ cur] . maxx= max ( tree[ cur] . maxx, tree[ tree[ cur] . ls] . maxx) ;
tree[ cur] . minn= min ( tree[ cur] . minn, tree[ tree[ cur] . ls] . minn) ;
tree[ cur] . maxx= max ( tree[ cur] . maxx, tree[ tree[ cur] . rs] . maxx) ;
tree[ cur] . minn= min ( tree[ cur] . minn, tree[ tree[ cur] . rs] . minn) ;
}
long long solve ( int cur) {
long long res= - inf;
if ( tree[ cur] . ls) res= max ( res, solve ( tree[ cur] . ls) ) ;
if ( tree[ cur] . rs) res= max ( res, solve ( tree[ cur] . rs) ) ;
if ( tree[ cur] . val> 0 ) {
long long lmin= inf, rmax= - inf;
if ( tree[ cur] . ls) lmin= tree[ tree[ cur] . ls] . minn;
else lmin= sum[ cur- 1 ] ;
rmax= max ( sum[ cur] , tree[ tree[ cur] . rs] . maxx) ;
res= max ( res, tree[ cur] . val* ( rmax- lmin) ) ;
}
else {
long long lmax= - inf, rmin= inf;
if ( tree[ cur] . ls) lmax= tree[ tree[ cur] . ls] . maxx;
else lmax= sum[ cur- 1 ] ;
rmin= min ( sum[ cur] , tree[ tree[ cur] . rs] . minn) ;
res= max ( res, tree[ cur] . val* ( rmin- lmax) ) ;
}
pushup ( cur) ;
return res;
}
} dical;
int main ( )
{
read ( n) ;
for ( int i= 1 ; i<= n; i++ ) read ( a[ i] ) ;
for ( int i= 1 ; i<= n; i++ ) read ( b[ i] ) , sum[ i] = sum[ i- 1 ] + b[ i] ;
dical. build ( a, n) ;
printf ( "%lld\n" , dical. solve ( dical. root) ) ;
}