「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 表达:

\[[j-1]_x! \cdot G_{i-j+1}(x)\cdot [n-i]_x!\cdot \frac{[n]_x!}{[j-1]_x![i-j+1]_x![n-i]_x!}=\frac{[n]_x!}{[i-j+1]_x} \]

类似地,计算后缀的前缀最大值时,同样有 \(H_m(x)=[m-1]_x!x^{m-1}\),并且:

\[[i-1]_x!\cdot H_{j-i+1}(x)\cdot [n-j]_x!\cdot\frac{[n]_x!}{[i-1]_x![j-i+1]_x![n-j]_x!}=\frac{x^{j-i}[n]_x!}{[j-i+1]_x} \]

注意到 \([n]_x!\) 可以 \(O(nK)\) 计算。而对于任意的 \(m\)\(\frac{[n]_x!}{[m]_x}=\frac{[n]_x!(1-x)}{1-x^m}\) 也可以 \(O(K)\) 计算。

对于某个 \(i\),求答案就只需要计算:

\[[x^K]\left([n]_x!+\sum_{m=2}^i\frac{[n]_x!}{[m]_x}+\sum_{m=2}^{n-i+1}\frac{x^{m-1}[n]_x!}{[m]_x}\right) \]

预处理系数即可 \(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;
}
posted @ 2023-03-17 21:51  crashed  阅读(44)  评论(0编辑  收藏  举报