Loading

题解—Cicada与排序

这个题当时非常蒙蔽,现在在看就比较好理解了。

首先用期望的线性性拆开,变成枚举每个数然后计算每个数处于每个位置上面的概率。

考虑模拟归并排序的过程,发现我们要搜的就是归并排序的一条链。

设计\(f_i\)表示在当前归并时处于相同的数中第\(i\)个的概率。

那么转移的时候,可以列出方程

\[f_{i+j}\leftarrow f_i*possible_{i,j} \]

所以发现需要一个数组 ,可以一边选择了\(i\) 个数,另一边选择了 \(j\) 个数的时候,选择一边的概率。

然后还能发现当另一边选完了之后,只能从一边选择,所以可以设计状态 \(dp_{i,j,0/1}\) 表示 左边选了 \(i\) 个, 右边选择了 \(j\) 个,右边是否选完的概率。

可以列出以下转移

\[dp_{i,j,0}*\frac{1}{2} \rightarrow dp_{i+1,j,0},dp_{i,j+1,0},dp_{i,j+1,1} \]

\[dp_{i,j,1} \rightarrow dp_{i,j+1,1} \]

但是注意左边右边是相对的,左边的真正含义是选择递归的那一边。

可以参考代码进行理解。

code
#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 = 5e2 + 10 ;
const int P = 998244353 ;
const int V = 499122177 ;

inline int A( int a , int b ){ return ( a + b ) >= P ? ( a + b - P ) : ( a + b ) ; }
inline int T( int a , int b ){ return ( 1ll * a * b ) - ( ( 1ll * a * b ) / P ) * P ; }
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 , a [N] , f [N] , g [N] , dp [N][N][2] ;

void sc(){
	n = read() ;
	for( R i = 1 ; i <= n ; i ++ ) a [i] = read() ;
}

void slv( int l , int r , int pos ){
	if( l == r ) return assert( pos == l ) , memset( f , 0 , sizeof( f ) ) , f [1] = 1 , void() ;
	int mid = ( l + r ) >> 1 ;
	if( pos <= mid ){
		slv( l , mid , pos ) ;
		for( R i = 1 ; i <= r - l + 1 ; i ++ ) g [i] = 0 ;

		int last = 0 ;
		for( R i = mid + 1 ; i <= r ; i ++ ) last = ( ( a [i] == a [pos] ) ? i : last ) ;
		if( !last ) return ;

		for( R i = l , cntl = 0 ; i <= mid ; i ++ ){
			if( a [i] != a [pos] ) continue ;
			cntl ++ , g [cntl] = A( g [cntl] , T( dp [cntl - 1][0][last == 0] , T( f [cntl] , V ) ) ) ;
			for( R j = mid + 1 , cntr = 0 ; j <= r ; j ++ ){
				if( a [j] != a [pos] ) continue ;
				cntr ++ ;
				int xs = T( dp [cntl - 1][cntr][j == last] , ( ( j == last ) ? 1 : V ) ) ;
				g [cntl + cntr] = A( g [cntl + cntr] , T( f [cntl] , xs ) ) ;
			}
		} 
	} else{
		slv( mid + 1 , r , pos ) ;
		for( R i = 1 ; i <= r - l + 1 ; i ++ ) g [i] = 0 ;		

		int last = 0 ;
		for( R i = l ; i <= mid ; i ++ ) last = ( ( a [i] == a [pos] ) ? i : last ) ;
		if( !last ) return ;
		
		for( R i = mid + 1 , cntr = 0 ; i <= r ; i ++ ){
			if( a [i] != a [pos] ) continue ;
			cntr ++ , g [cntr] = A( g [cntr] , T( dp [cntr - 1][0][last == 0] , T( f [cntr] , V ) ) ) ;
			for( R j = l , cntl = 0 ; j <= mid ; j ++ ){
				if( a [j] != a [pos] ) continue ;
				cntl ++ ;
				int xs = T( dp [cntr - 1][cntl][j == last] , ( ( j == last ) ? 1 : V ) ) ;
				g [cntl + cntr] = A( g [cntl + cntr] , T( xs , f [cntr] ) ) ; 
			}
		}
	}
	
	for( R i = 1 ; i <= r - l + 1 ; i ++ ) f [i] = g [i] ;
}

void work(){
	dp [0][0][0] = dp [0][0][1] = 1 ;
	
	for( R i = 0 ; i <= n ; i ++ ) for( R j = 0 ; j <= n ; j ++ ){
		dp [i][j + 1][0] = A( dp [i][j + 1][0] , T( V , dp [i][j][0] ) ) ;
		dp [i][j + 1][1] = A( dp [i][j + 1][1] , T( V , dp [i][j][0] ) ) ;
		dp [i + 1][j][0] = A( dp [i + 1][j][0] , T( V , dp [i][j][0] ) ) ;
		dp [i + 1][j][1] = A( dp [i + 1][j][1] , dp [i][j][1] ) ;
	}  for( R i = 1 ; i <= n ; i ++ ){
		slv( 1 , n , i ) ;
		int val = a [i] , cnt = 0 ;
		for( R j = 1 ; j <= n ; j ++ ) cnt += ( a [j] < val ) ;
		int ans = 0 ;
		for( R j = cnt + 1 ; j <= n ; j ++ ) ans = A( ans , T( j , f [j - cnt] ) ) ;
		printf( "%d%c" , ans , ( i == n ) ? '\n' : ' ' ) ;
	} 
}

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