「USACO19DEC」Tree Depth P
题目
点这里看题目。
给定正整数 \(n\) 和非负整数 \(k\),对于所有 \(i=1,2,3,\dots,n\),求出所有长度为 \(n\) 且逆序对数为 \(k\) 的排列构建的小根笛卡尔树中第 \(i\) 个位置的深度之和(根的深度为 \(1\))。
所有数据满足 \(1\le n\le 300,0\le k\le \binom{n}{2}\)。
分析
注意到,结点 \(i\) 的深度就是前缀 \([1,i]\) 的后缀最小值个数加 \([i,n]\) 的前缀最小值个数 \(-1\)。
以前缀的后缀最小值为例。枚举 \(1\le j<i\),我们钦定 \(\forall j<k\le i\) 都满足 \(p_j<p_k\)。注意到 \([j,i]\) 和外部是独立的,我们首先考虑 \([j,i]\) 的排列情况。
设 \(m=i-j+1\),我们用 \(G_m(x)\) 表示所有满足 \(p_1=1\) 的长度为 \(m\) 的排列 \(p\) 按照 \(x^{\sigma(p)}\) 求和的方案数。既然有逆序对数,不妨考虑使用 q-analog 技术,可以立即得到 \(G_m(x)=[m-1]_x!\)。
那么,将 \([j,i]\) 和前缀 \([1,j-1]\) 与后缀 \([i+1,n]\) 组合起来时,我们还需要考虑交叉逆序对,这个仍然可以用 q-analog 表达:
类似地,计算后缀的前缀最大值时,同样有 \(H_m(x)=[m-1]_x!x^{m-1}\),并且:
注意到 \([n]_x!\) 可以 \(O(nK)\) 计算。而对于任意的 \(m\),\(\frac{[n]_x!}{[m]_x}=\frac{[n]_x!(1-x)}{1-x^m}\) 也可以 \(O(K)\) 计算。
对于某个 \(i\),求答案就只需要计算:
预处理系数即可 \(O(n^2)\) 回答。
代码
#include <cstdio>
#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )
const int MAXN = 305, MAXK = MAXN * MAXN;
template<typename _T>
inline void Read( _T &x ) {
x = 0; char s = getchar(); bool f = false;
while( ! ( '0' <= s && s <= '9' ) ) { f = s == '-', s = getchar(); }
while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
if( f ) x = -x;
}
template<typename _T>
inline void Write( _T x ) {
if( x < 0 ) putchar( '-' ), x = -x;
if( 9 < x ) Write( x / 10 );
putchar( x % 10 + '0' );
}
int g[MAXN], h[MAXN];
int F[MAXK], tmp[MAXK];
int N, K, mod;
inline int Mul( int x, const int &v ) { return 1ll * x * v % mod; }
inline int Sub( int x, const int &v ) { return ( x -= v ) < 0 ? x + mod : x; }
inline int Add( int x, const int &v ) { return ( x += v ) >= mod ? x - mod : x; }
inline int& MulEq( int &x, const int &v ) { return x = 1ll * x * v % mod; }
inline int& SubEq( int &x, const int &v ) { return ( x -= v ) < 0 ? ( x += mod ) : x; }
inline int& AddEq( int &x, const int &v ) { return ( x += v ) >= mod ? ( x -= mod ) : x; }
int main() {
Read( N ), Read( K ), Read( mod );
F[0] = 1;
rep( i, 1, N ) {
rep( j, 0, K ) {
tmp[j] = F[j];
if( j ) AddEq( tmp[j], tmp[j - 1] );
if( j >= i ) SubEq( tmp[j], F[j - i] );
}
rep( j, 0, K ) F[j] = tmp[j];
}
rep( m, 1, N ) {
rep( j, 0, K ) {
tmp[j] = F[j];
if( j >= m ) AddEq( tmp[j], tmp[j - m] );
}
per( j, K, 1 )
SubEq( tmp[j], tmp[j - 1] );
g[m] = tmp[K];
h[m] = K >= m - 1 ? tmp[K - m + 1] : 0;
}
rep( i, 1, N ) {
int ans = F[K];
rep( m, 2, i ) AddEq( ans, g[m] );
rep( m, 2, N - i + 1 ) AddEq( ans, h[m] );
Write( ans ), putchar( " \n"[i == N] );
}
return 0;
}