「LGP8554」心跳

题目

点这里看题目。


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

对于一个长度为 l 的排列 p,可以生成一个长度为 l 的序列 a,其中 ai=f(p¯i)。此处,p¯ip 去掉第 i 个元素后剩下的长度为 l1 的序列。

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

所有数据满足 2mn2000

分析

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

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

  1. 我们几乎可以直接枚举 f0 来计数。

  2. 我们需要对于“可以同时被 f0=minaf0=1+mina 构造出来的 a”做额外处理。

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

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

  1. p1 一定是前缀最大值

  2. 如果某个位置 i 满足 aif0,则任意一个构造 a 的排列 p 中,pi 都为前缀最大值

    说明:删除一个不为前缀最大值的位置 x,产生的 f(p¯x) 必然为 f0

  3. 如果某个位置 x 满足 px 是前缀最大值,则 x+axf0+1n[x,x+axf0+1] 范围内只有 x 一个位置为前缀最大值

    说明:删除 px 后,原序列至少有 f01 个前缀最大值,而 aif0+1 个新来的前缀最大值必然出现在 x 和下一个前缀最大值之间。

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

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

t=1f0kf0t(k+t1t1)[xn2k][x21x(x31x)t1(1+x)t]

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


类似地,f0=1+mina 的情况几乎就是上述情况的翻版:忽略 p1 的细节后,f0=1+mina 的区别就在于 ax=mina=f01 的位置也是必须前缀最大值,这样的位置就对应了长度为 1 的段。

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

f0=minaf0=1+mina 合起来,就可以得到繁琐的讨论:

  1. 如果 p1 对应的段长度 2

    t=1f0kf0t(k+t1t1)[xn2k][x21x(x31x+x)t1(1+x)t]

  2. 如果 p1 对应的段长度为 1,且 t=1

    [n12(f01)]

  3. 如果 p1 对应的段长度为 1,且 1<t<f0,此时可以容斥计算:

    t=2f01kf0t(k+t1t1)[xn2k][x(x31x+x)t1(1+x)t]t=2f01kf0t(k+t2t2)[xn2k][x2(x31x+x)t1(1+x)t1]

  4. 如果 p1 对应的段长度为 1,且 t=f0,此时可以直接计算:

    k0(k+f02f02)[xn2k][x(x31x+x)t1(1+x)t1]

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

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


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

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

Property.

不存在满足如下性质的 a

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


假设存在。设 v=mina

  1. 考察 p。若 px 位置为 p 中前缀最大值,则 x<npx+1 一定不是 p 中前缀最大值

    这一点可以导出 2vnvn2

  2. a 中有 αvβv+1。容易发现,有 α+βn

    此外,任意一个 ax=v+1x 必然类似地满足 pxp 中前缀最大值,x+2npx+1,px+2 一定不是 p 中前缀最大值。于是 ax+1=ax+2=v,导出 2βα,接着可以导出 βn3

    最后,在 q 中,前缀最大值至少有 nβ 个,所以 v+1nβ,于是有 v23n1

假如 23n1n2,可以解得 n6。所以当 n>6 时,这样的 a 不存在;当 n6 时,经过暴力验证也可以发现这样的 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 @   crashed  阅读(56)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示