题解—biology
这道题做的真是失败。
考场上一直在想如何消除后效性,结果我发现一个事情,我一直在考虑把一个不可能的思路深度化。
其实还是对自己的能力不够自信,看到这条路不行就赶紧回撤啊。
最后由于紧张,忘了打我原本推出来的柿子,还认为会对后面产生影响,打了个\(dfs\)。
一乱之下设计好的特殊性之分也没拿到。
特殊性质利用指针单调不降转移就行,但是其实复杂度不太对,可是出题人一看就不会卡这种\(sb\)才会想出来的算法,所以复杂度\(O\)(能过)
其实普通\(dp\)是显然的,只需要设计状态是到达当前节点的最小值转移就好了。
主要是里面的绝对值很难受,我考场上一直想用一个变量操作一手,没想到这波炸大了,废了大量时间,没想出来。
发现其实一个思路思考很多之后就容易划水,浪费了很多时间。
正确的想法是用四个方向的树状数组分别维护最值,通过两步把绝对值拆开。
又学到一种处理绝对值的方法。
具体的说,每次把一个变量\(f-x-y,f-x+y,f+x-y,f+x+y\)分别扔到一二三四里面
然后查的时候带着\(max+x+y,max+x-y,max-x+y,max-x-y\)查询。
分别对应了四种情况的最值。
然后发现树状数组不好维护区间最值,但是很容易维护前缀最值。
还能发现其实我们查的都是前缀,只是不同方向上的前缀。
所以开四个树状数组还维护。
这个能拿\(80pts\)。
code
#include <cstdio>
#include <algorithm>
#include <cstring>
#define R register int
#define int long long
#define printf Ruusupuu = printf
#define scanf Ruusupuu = scanf
int Ruusupuu ;
using namespace std ;
typedef long long L ;
typedef long double D ;
typedef pair< int , int > PI ;
const int N = 2e3 + 10 ;
inline int read(){
int w = 0 ; bool fg = 0 ; char ch = getchar() ;
while( ch > '9' || ch < '0' ) fg |= ( ch == '-' ) , ch = getchar() ;
while( ch <= '9' && ch >= '0' ) w = ( w << 1 ) + ( w << 3 ) + ( ch - '0' ) , ch = getchar() ;
return fg ? -w : w ;
}
int n , m , a [N][N] , b [N][N] , mx [N][N] , top , zop , ts , anss , poolans [N * N] , pooli [N * N] , poolj [N * N] , pp ;
bool fg = 1 ;
struct ZT{ int m , b , i , j , ans ; } l [N * N] ;
struct ST{ int val , beg , en ; } st [N * N] ;
inline bool cmp( ZT a , ZT b ){ return ( ( a.m == b.m ) ? ( ( a.i == b.i ) ? ( a.j < b.j ) : ( a.i < b.i ) ) : ( a.m < b.m ) ) ; }
#define Abs( x ) ( ( x >= 0 ) ? ( (x) ) : ( -(x) ) )
#define Max( x , y ) ( ( x >= y ) ? x : y )
inline int lb( int x ){ return ( x & (-x) ) ; }
void sc(){
n = read() , m = read() ;
for( R i = 1 ; i <= n ; i ++ )
for( R j = 1 ; j <= m ; j ++ )
a [i][j] = read() , l [++ top].m = a [i][j] , l [top].i = i , l [top].j = j , fg &= ( a [i][j] == i ) ;
for( R i = 1 ; i <= n ; i ++ )
for( R j = 1 ; j <= m ; j ++ )
b [i][j] = read() , l [++ zop].b = b [i][j] ;
}
void onnea(){
for( R i = 1 ; i <= n ; i ++ ){
R j = m ;
for( R k = 1 ; k <= m ; k ++ ){
if( i == 1 ) mx [i][k] = b [i][k] ;
else{
for( R l = j - 1 ; l >= 1 ; l -- )
if( mx [i - 1][j] + ( Abs( j - k ) ) <= mx [i - 1][l] + ( Abs( k - ( l ) ) ) ) j = l ;
mx [i][k] = ( b [i][k] + mx [i - 1][j] + Abs( j - k ) + 1 ) ;
anss = Max( anss , mx [i][k] ) ;
}
//printf( "%lld %lld %lld %lld\n" , i , k , j , mx [i][k] ) ;
}
}
printf( "%lld\n" , anss ) ;
}
int ans , tz1 [N][N] , tz2 [N][N] , tz3 [N][N] , tz4 [N][N] ;
inline void add1( int x , int y , int val ){
for( ; x <= n ; x += lb( x ) )
for( R j = y ; j <= m ; j += lb( j ) )
tz1 [x][j] = Max( tz1 [x][j] , val ) ;
}
inline void add2( int x , int y , int val ){
y = m - y + 1 ;
for( ; x <= n ; x += lb( x ) )
for( R j = y ; j <= m ; j += lb( j ) )
tz2 [x][j] = Max( tz2 [x][j] , val ) ;
}
inline void add3( int x , int y , int val ){
x = n - x + 1 ;
for( ; x <= n ; x += lb( x ) )
for( R j = y ; j <= m ; j += lb( j ) )
tz3 [x][j] = Max( tz3 [x][j] , val ) ;
}
inline void add4( int x , int y , int val ){
x = n - x + 1 , y = m - y + 1 ;
for( ; x <= n ; x += lb( x ) )
for( R j = y ; j <= m ; j += lb( j ) )
tz4 [x][j] = Max( tz4 [x][j] , val ) ;
}
inline int as1( int x , int y ){
int ans = 0 ;
for( ; x ; x -= lb( x ) )
for( R j = y ; j ; j -= lb( j ) )
ans = Max( ans , tz1 [x][j] ) ;
return ans ;
}
inline int as2( int x , int y ){
int ans = 0 ; y = m - y + 1 ;
for( ; x ; x -= lb( x ) )
for( R j = y ; j ; j -= lb( j ) )
ans = Max( ans , tz2 [x][j] ) ;
return ans ;
}
inline int as3( int x , int y ){
int ans = 0 ; x = n - x + 1 ;
for( ; x ; x -= lb( x ) )
for( R j = y ; j ; j -= lb( j ) )
ans = Max( ans , tz3 [x][j] ) ;
return ans ;
}
inline int as4( int x , int y ){
int ans = 0 ; x = n - x + 1 , y = m - y + 1 ;
for( ; x ; x -= lb( x ) )
for( R j = y ; j ; j -= lb( j ) )
ans = Max( ans , tz4 [x][j] ) ;
return ans ;
}
int asks( int x , int y ){
return Max ( Max( as1( x , y ) + x + y , as2( x , y ) + x - y ) , Max( as3( x , y ) - x + y , as4( x , y ) - x - y ) ) ;
}
void adds(){
for( R i = 1 ; i <= pp ; i ++ ){
int x = pooli [i] , y = poolj [i] , val = poolans [i] ;
add1( x , y , val - x - y ) , add2( x , y , val - x + y ) , add3( x , y , val + x - y ) , add4( x , y , val + x + y ) ;
}
}
void work(){
if( fg ) onnea() , exit( 0 ) ;
sort( l + 1 , l + 1 + top , cmp ) ;
// for( R i = 1 ; i <= top ; i ++ ) printf( "TS%lld %lld %lld\n" , l [i].m , l [i].i , l [i].j ) ;
R j = 0 ;
for( R i = 1 ; i <= top ; i = j + 1 ){
if( !l [i].m ) { j = i + 1 ; continue ; }
j = i ;
for( ; l [j].m == l [i].m ; j ++ ) ;
if( l [j].m != l [i].m ) j -- ;
st [++ ts].val = l [i].m ;
st [ts].beg = i ;
st [ts].en = j ;
}
// for( R i = 1 ; i <= ts ; i ++ )
// printf( "%lld %lld %lld\n" , st [i].beg , st [i].en , st [i].val ) ;
for( R i = 1 ; i <= ts ; i ++ ){
pp = 0 ;
for( R j = st [i].beg ; j <= st [i].en ; j ++ ){
//printf( "%lld %lld %lld %lld\n" , j , l [k].ans , l [k].i , l [k].j ) ;
if( i == 1 ) l [j].ans = l [j].b ;
else {
l [j].ans = asks( l [j].i , l [j].j ) ;
l [j].ans += l [j].b ;
}
poolans [++ pp] = l [j].ans , pooli [pp] = l [j].i , poolj [pp] = l [j].j ;
// printf( "%lld %lld %lld\n" , l [j].i , l [j].j , l [j].ans ) ;
ans = Max( ans , l [j].ans ) ;
} adds() ;
}
printf( "%lld\n" , ans ) ;
}
signed main(){
sc() ;
work() ;
return 0 ;
}
其实正解和宝藏这道题有异\(dp\)同工之妙
我们直接用四个变量来维护四个方向上的最值。
如果我们从错误的方向上面的到了答案,那么他一定不是asks里面的最大值,因为错误的方向只会让一个绝对值变成负的,他肯定不会更新答案。
其实很多最优值问题,在想用数据结构维护什么东西的时候,想想能不能利用最有解的必然性来解题,减少时间复杂度。
$The \ light \ has \ betrayed \ me$

浙公网安备 33010602011771号