Loading

题解—排列

好像并不是所谓的笛卡尔树上dp,不过确实有点那种意思。

但是比起来糖果还差很多。

这道题主要看出来一个性质就可以转移。

首先可以让最大值划分区间,因为可以发现最大值统域的一个区间和别的区间是独立的(虽然我没看出来这个)。

然后是关键的性质,就是由一个区间最大值统域的区间,两边要不是挨着边界,要不是挨着更大值。

如果挨着更大值,那么,最大值会在挨着更大值的子区间消去后的下一次消去。

那就很好转移了。

设计状态 \(f_{i,j,0/1,0/1}\) 代表长度为 \(i\) 的区间,至多通过\(j\)次被消掉,两边分别挨着边界还是更大值 的方案数。

如果你设计的为恰好的话,那么需要一个简单的前缀和来优化一下转移(其实前缀和就是至多,这个定义就是不考虑恰好只考虑至多)

如果设计为至多,那么需要一个简单的差分得到恰好从而转移。

所以两种状态设计没什么区别。

解释一下那个长的转移,就是让都小于等于j次的减去都是j次的,剩下的就都是合法的了。

这种信息很少的题,需要自己观察性质,找到什么状态的本质是相同的,从而设计状态进行转移。

code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <assert.h>
#include <algorithm>
#define R register int 
#define scanf Ruusupuu = scanf
#define freopen rsp_5u = freopen
#define fre(x) freopen( #x".in" , "r" , stdin ) , freopen( #x".out" , "w" , stdout )

int Ruusupuu ;
FILE * rsp_5u ;

using namespace std ;
typedef long long L ;
typedef double D ;
const int N = 1e3 + 10 ;

inline int read(){
	int w = 0 ; bool fg = 0 ; char ch = getchar() ;
	while( ch < '0' || ch > '9' ) fg |= ( ch == '-' ) , ch = getchar() ;
	while( ch >= '0' && ch <= '9' ) w = ( w << 1 ) + ( w << 3 ) + ( ch ^ 48 ) , ch = getchar() ;
	return fg ? -w : w ;
}

int n , m , P , jc [N] , inv [N] , f [N][20][5][5] ;

inline int A( int a , int b ){ return ( a + b ) >= P ? ( a + b - P ) : ( a + b ) ; }
inline int E( int a , int b ){ return ( a - b ) < 0 ? ( a - b + P ) : ( a - b ) ; }
inline int T( int a , int b ){ return ( 1ll * a * b ) - ( ( 1ll * a * b ) / P ) * P ; }
inline int C( int n , int m ){ return T( jc [n] , T( inv [m] , inv [n - m] ) ) ; }

void sc(){
	n = read() , m = read() , P = read() ;
	if( m > ceil( log2( n ) ) ) puts( "0" ) , exit( 0 ) ;
	jc [0] = jc [1] = inv [0] = inv [1] = 1 ;
	for( R i = 2 ; i <= n ; i ++ ) jc [i] = T( jc [i - 1] , i ) , inv [i] = T( ( P - P / i ) , inv [P % i] ) ;
	for( R i = 2 ; i <= n ; i ++ ) inv [i] = T( inv [i] , inv [i - 1] ) ;
}

void work(){
	for( R i = 0 ; i <= m ; i ++ ) f [0][i][0][0] = f [0][i][1][1] = f [0][i][1][0] = f [0][i][0][1] = 1 ;
	for( R i = 1 ; i <= n ; i ++ ) for( R j = 1 ; j <= m ; j ++ ) for( R k = 1 ; k <= i ; k ++ ){
		f [i][j][0][0] = A( f [i][j][0][0] , T( C( i - 1 , k - 1 ) , 
			E( T( f [k - 1][j][0][0] , f [i - k][j][0][0] ) , T( E( f [k - 1][j][0][0] , f [k - 1][j - 1][0][0] ) , E( f [i - k][j][0][0] , f [i - k][j - 1][0][0] ) ) ) ) ) ;
		
		f [i][j][0][1] = A( f [i][j][0][1] , T( C( i - 1 , k - 1 ) , T( f [k - 1][j - 1][0][0] , f [i - k][j][0][1] ) ) ) ;
		f [i][j][1][0] = A( f [i][j][1][0] , T( C( i - 1 , k - 1 ) , T( f [k - 1][j][1][0] , f [i - k][j - 1][0][0] ) ) ) ;
		f [i][j][1][1] = A( f [i][j][1][1] , T( C( i - 1 , k - 1 ) , T( f [k - 1][j][1][0] , f [i - k][j][0][1] ) ) ) ;
	} printf( "%d\n" , E( f [n][m][1][1] , f [n][m - 1][1][1] ) ) ;
}

signed main(){
//	fre( in ) ;
	sc() ;
	work() ;
	return 0 ;
}
posted @ 2021-09-07 08:17  Soresen  阅读(46)  评论(0)    收藏  举报