题解—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 ;
}
$The \ light \ has \ betrayed \ me$