Loading

题解—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里面的最大值,因为错误的方向只会让一个绝对值变成负的,他肯定不会更新答案。

其实很多最优值问题,在想用数据结构维护什么东西的时候,想想能不能利用最有解的必然性来解题,减少时间复杂度。

posted @ 2021-07-11 21:27  Soresen  阅读(61)  评论(0)    收藏  举报