「LGP8554」心跳

题目

点这里看题目。


对于一个长度为 \(l\) 的序列 \(p\),设 \(f(p)\)\(p\) 的前缀最大值的个数。

对于一个长度为 \(l\) 的排列 \(p\),可以生成一个长度为 \(l\) 的序列 \(a\),其中 \(a_i=f(\overline p_i)\)。此处,\(\overline p_i\)\(p\) 去掉第 \(i\) 个元素后剩下的长度为 \(l-1\) 的序列。

给定正整数 \(n\),求出可以由长度为 \(n\) 的排列生成、最小值 \(\ge m\) 的本质不同的 \(a\) 的数量,答案对于 \(998244353\) 取模。

所有数据满足 \(2\le m\le n\le 2000\)

分析

不妨先来讨论可以被构造出来的 \(a\) 的充要条件。

我们注意到如果 \(p\) 可以构造出 \(a\),且 \(f(p)=f_0\),则 \(f_0\) 要么为 \(\min a\),要么为 \(1+\min a\),这一点无需多加说明。这导出了两点:

  1. 我们几乎可以直接枚举 \(f_0\) 来计数。

  2. 我们需要对于“可以同时被 \(f_0=\min a\)\(f_0=1+\min a\) 构造出来的 \(a\)”做额外处理。

    具体来说,我们要么证明不可能出现这样的 \(a\),要么进行去重。具体走哪一条路,取决于计数条件的难易程度。

放一放第二个问题先。对于 \(a\),尝试在 \(f_0=\min a\) 时进行构造。则可以观察得到:

  1. \(p_1\) 一定是前缀最大值

  2. 如果某个位置 \(i\) 满足 \(a_i\neq f_0\),则任意一个构造 \(a\) 的排列 \(p\) 中,\(p_i\) 都为前缀最大值

    说明:删除一个不为前缀最大值的位置 \(x\),产生的 \(f(\overline p_x)\) 必然为 \(f_0\)

  3. 如果某个位置 \(x\) 满足 \(p_x\) 是前缀最大值,则 \(x+a_x-f_0+1\le n\)\([x,x+a_x-f_0+1]\) 范围内只有 \(x\) 一个位置为前缀最大值

    说明:删除 \(p_x\) 后,原序列至少有 \(f_0-1\) 个前缀最大值,而 \(a_i-f_0+1\) 个新来的前缀最大值必然出现在 \(x\) 和下一个前缀最大值之间。

这意味着,满足 \(i=1\)\(a_i\neq f_0\) 的位置必然会被选为前缀最大值,此外可能还需要加入一些 \(a_x=f_0\) 的位置作为前缀最大值,以满足 “\(f_0\) 个前缀最大值的要求”;钦定为前缀最大值的位置 \(x\) 相当于将 \([x,x+a_x-f_0+1]\) 的位置绑定为一段,且一个位置不能被绑定在两段以内。可以发现这也是充分的。

注意到,“必然前缀最大值产生的段”的结构和 \(a\) 构成了双射,所以我们可以对其进行计数,并满足上述条件。枚举的思路是:确定必然前缀最大值的结构;段之间可以插入 \(f_0\),这样的位置就有 \(t\) 个,对于每一个可以插入 \(f_0\) 的位置,枚举其插入的 \(f_0\) 的个数的奇偶性;枚举有多少个长度为 \(2\)\(f_0\) 的段。于是,可以写出答案为:

\[\sum_{t=1}^{f_0}\sum_{k\ge f_0-t}\binom{k+t-1}{t-1}[x^{n-2k}]\left[\frac{x^2}{1-x}\cdot\left(\frac{x^3}{1-x}\right)^{t-1}(1+x)^t\right] \]

其中 \(k\) 就是在枚举长度为 \(2\) 的段的数量。后面的生成函数描述了除了长度为 \(2\)\(f_0\) 的段以外的结构。


类似地,\(f_0=1+\min a\) 的情况几乎就是上述情况的翻版:忽略 \(p_1\) 的细节后,\(f_0=1+\min a\) 的区别就在于 \(a_x=\min a=f_0-1\) 的位置也是必须前缀最大值,这样的位置就对应了长度为 \(1\) 的段。

但是,但是,当我们重新讨论 \(f_0=1+\min a\) 的情况时,我们发现 \(p_1\) 对应的段如果为长度为 \(1\),则会出现相当多的例外。这是因为,\(p_1\) 之前没有任何元素,所以删除它后 \(p_2\) 一定是前缀最大值,而 \(a_1=f_0-1\),所以 \(p_2\) 必须是“必然前缀最大值”。于是,我们可以对于 \(p_1\) 对应长度为 \(1\) 的段的情况进行一个单独的计算。

\(f_0=\min a\)\(f_0=1+\min a\) 合起来,就可以得到繁琐的讨论:

  1. 如果 \(p_1\) 对应的段长度 \(\ge 2\)

    \[\sum_{t=1}^{f_0}\sum_{k\ge f_0-t}\binom{k+t-1}{t-1}[x^{n-2k}]\left[\frac{x^2}{1-x}\cdot \left(\frac{x^3}{1-x}+x\right)^{t-1}(1+x)^t\right] \]

  2. 如果 \(p_1\) 对应的段长度为 \(1\),且 \(t=1\)

    \[[n-1\ge 2(f_0-1)] \]

  3. 如果 \(p_1\) 对应的段长度为 \(1\),且 \(1<t<f_0\),此时可以容斥计算:

    \[\begin{aligned} &\sum_{t=2}^{f_0-1}\sum_{k\ge f_0-t}\binom{k+t-1}{t-1}[x^{n-2k}]\left[x\left(\frac{x^3}{1-x}+x\right)^{t-1}(1+x)^t\right]\\ -&\sum_{t=2}^{f_0-1}\sum_{k\ge f_0-t}\binom{k+t-2}{t-2}[x^{n-2k}]\left[x^2\left(\frac{x^3}{1-x}+x\right)^{t-1}(1+x)^{t-1}\right] \end{aligned} \]

  4. 如果 \(p_1\) 对应的段长度为 \(1\),且 \(t=f_0\),此时可以直接计算:

    \[\sum_{k\ge 0}\binom{k+f_0-2}{f_0-2}[x^{n-2k}]\left[x\left(\frac{x^3}{1-x}+x\right)^{t-1}(1+x)^{t-1}\right] \]

可以在 \(O(n^2)\) 的时间内预处理后面 GF 的系数。直接枚举看似是 \(O(n^3)\) 的,但是注意到 \(f_0\) 实际上不需要直接枚举,我们只需要由 \(t,k\) 确定 \(f_0\) 的个数就行,于是复杂度可以被优化到 \(O(n^2)\)

顺便一说,上述分析并没有考虑 \(\min a\ge m\) 的条件。不过这并不复杂,对于 \(f_0>m\),用上面的方式计算即可;对于 \(f_0=m\),单独计算 \(f_0=\min a=m\) 的数量即可。


最后,处理 \(a\) 可能算重的历史遗留问题。考虑到直接计算都非常困难,我们还是倾向于证明“不会算重”。

证明思路并不复杂,用不等式导出矛盾即可,以下为详细过程:

Property.

不存在满足如下性质的 \(a\)

存在 \(f(p)=\min a\)\(f(q)=1+\min a\) 的排列 \(p,q\),使得 \(a\) 可以由 \(p,q\) 构造。


假设存在。设 \(v=\min a\)

  1. 考察 \(p\)。若 \(p_x\) 位置为 \(p\) 中前缀最大值,则 \(x<n\)\(p_{x+1}\) 一定不是 \(p\) 中前缀最大值

    这一点可以导出 \(2v\le n\Rightarrow v\le \frac{n}{2}\)

  2. \(a\) 中有 \(\alpha\)\(v\)\(\beta\)\(v+1\)。容易发现,有 \(\alpha+\beta\le n\)

    此外,任意一个 \(a_x=v+1\)\(x\) 必然类似地满足 \(p_x\)\(p\) 中前缀最大值,\(x+2\le n\)\(p_{x+1},p_{x+2}\) 一定不是 \(p\) 中前缀最大值。于是 \(a_{x+1}=a_{x+2}=v\),导出 \(2\beta\le \alpha\),接着可以导出 \(\beta\le \frac n 3\)

    最后,在 \(q\) 中,前缀最大值至少有 \(n-\beta\) 个,所以 \(v+1\ge n-\beta\),于是有 \(v\ge \frac 2 3n-1\)

假如 \(\frac 2 3n-1\le \frac n 2\),可以解得 \(n\le 6\)。所以当 \(n>6\) 时,这样的 \(a\) 不存在;当 \(n\le 6\) 时,经过暴力验证也可以发现这样的 \(a\) 不存在。

于是这个恼人的问题就解决了。

代码

#include <cstdio>
#include <iostream>

#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 mod = 1e9 + 7;
const int MAXN = 4e3 + 5;

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 poly[MAXN];
int coe1[MAXN][MAXN], coe2[MAXN][MAXN], coe3[MAXN][MAXN], coe4[MAXN][MAXN];

int C[MAXN][MAXN];

int N, M;

inline int Qkpow( int, int );
inline int Inv( const int &a ) { return Qkpow( a, mod - 2 ); }
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; }

inline int Qkpow( int base, int indx ) {
	int ret = 1;
	while( indx ) {
		if( indx & 1 ) MulEq( ret, base );
		MulEq( base, base ), indx >>= 1;
	}
	return ret;
}

inline void Init( const int &n ) {
	rep( i, 0, n ) {
		C[i][0] = C[i][i] = 1;
		rep( j, 1, i )
			C[i][j] = Add( C[i - 1][j], C[i - 1][j - 1] );
	}
	poly[0] = 1;
	rep( t, 1, n ) {
		per( i, n, 1 ) AddEq( poly[i], poly[i - 1] ); // *= ( 1 + x )
		rep( i, 0, n ) coe4[t][i] = poly[i];
		rep( i, 1, n ) AddEq( poly[i], poly[i - 1] ); // *= 1 / ( 1 - x )
		rep( i, 0, n ) coe1[t][i] = poly[i];
		per( i, n, 0 ) {
			int tmp = poly[i];
			if( i >= 1 ) SubEq( tmp, poly[i - 1] );
			if( i >= 2 ) AddEq( tmp, poly[i - 2] );
			poly[i] = tmp;
		} // *= x^2 - x + 1
		rep( i, 0, n ) coe2[t][i] = poly[i];
	}
	rep( i, 0, n ) poly[i] = ( i == 0 );
	rep( t, 1, n ) {
		rep( i, 1, n ) AddEq( poly[i], poly[i - 1] );
		per( i, n, 1 ) AddEq( poly[i], poly[i - 1] );
		rep( i, 0, n ) coe3[t][i] = poly[i];
	}
}

int main() {
	// freopen( "beats.in", "r", stdin );
	// freopen( "beats.out", "w", stdout );
	Read( N ), Read( M ), Init( N );
	int ans = 0;
	for( int t = 1 ; t <= N ; t ++ ) {
		for( int k = 0 ; N - 2 * k - t - 1 >= 0 ; k ++ ) {
			int l = std :: max( t, M + 1 ), r = std :: min( t + k, N );
			if( l <= r ) AddEq( ans, Mul( r - l + 1, Mul( C[k + t - 1][t - 1], coe1[t][N - 2 * k - t - 1] ) ) );
		}
		// if v == t
		if( M + 1 <= t )
			for( int k = 0 ; N - 2 * k - t >= 0 ; k ++ )
				AddEq( ans, Mul( C[k + t - 2][t - 2], coe2[t - 1][N - 2 * k - t] ) );
		// if 2 <= t < v
		if( t >= 2 )
			for( int k = 0 ; N - 2 * k - t >= 0 ; k ++ ) {
				int res = 0;
				AddEq( res, Mul( C[k + t - 1][t - 1], coe4[t][N - 2 * k - t] ) );
				if( N - 2 * k - t - 1 >= 0 )
					SubEq( res, Mul( C[k + t - 2][t - 2], coe2[t - 1][N - 2 * k - t - 1] ) );
				int l = std :: max( t + 1, M + 1 ), r = std :: min( N, t + k );
				if( l <= r ) AddEq( ans, Mul( res, r - l + 1 ) );
			}
		// if t == 1
		if( t == 1 ) 
			for( int v = M + 1 ; v <= N ; v ++ )
				AddEq( ans, N - 1 >= 2 * ( v - t ) );
	}
	for( int t = 1 ; t <= M ; t ++ )
		for( int k = M - t ; N - 2 * k - 3 * t + 1 >= 0 ; k ++ )
			AddEq( ans, Mul( C[k + t - 1][t - 1], coe3[t][N - 2 * k - 3 * t + 1] ) );
	Write( ans ), putchar( '\n' );
	return 0;
}
posted @ 2023-02-21 23:06  crashed  阅读(67)  评论(0)    收藏  举报