「LGP8554」心跳
题目
点这里看题目。
对于一个长度为 的序列 ,设 为 的前缀最大值的个数。
对于一个长度为 的排列 ,可以生成一个长度为 的序列 ,其中 。此处, 为 去掉第 个元素后剩下的长度为 的序列。
给定正整数 ,求出可以由长度为 的排列生成、最小值 的本质不同的 的数量,答案对于 取模。
所有数据满足 。
分析
不妨先来讨论可以被构造出来的 的充要条件。
我们注意到如果 可以构造出 ,且 ,则 要么为 ,要么为 ,这一点无需多加说明。这导出了两点:
-
我们几乎可以直接枚举 来计数。
-
我们需要对于“可以同时被 和 构造出来的 ”做额外处理。
具体来说,我们要么证明不可能出现这样的 ,要么进行去重。
具体走哪一条路,取决于计数条件的难易程度。
放一放第二个问题先。对于 ,尝试在 时进行构造。则可以观察得到:
-
一定是前缀最大值。
-
如果某个位置 满足 ,则任意一个构造 的排列 中, 都为前缀最大值。
说明:删除一个不为前缀最大值的位置 ,产生的 必然为 。
-
如果某个位置 满足 是前缀最大值,则 且 范围内只有 一个位置为前缀最大值。
说明:删除 后,原序列至少有 个前缀最大值,而 个新来的前缀最大值必然出现在 和下一个前缀最大值之间。
这意味着,满足 或 的位置必然会被选为前缀最大值,此外可能还需要加入一些 的位置作为前缀最大值,以满足 “ 个前缀最大值的要求”;钦定为前缀最大值的位置 相当于将 的位置绑定为一段,且一个位置不能被绑定在两段以内。可以发现这也是充分的。
注意到,“必然前缀最大值产生的段”的结构和 构成了双射,所以我们可以对其进行计数,并满足上述条件。枚举的思路是:确定必然前缀最大值的结构;段之间可以插入 ,这样的位置就有 个,对于每一个可以插入 的位置,枚举其插入的 的个数的奇偶性;枚举有多少个长度为 的 的段。于是,可以写出答案为:
其中 就是在枚举长度为 的段的数量。后面的生成函数描述了除了长度为 的 的段以外的结构。
类似地, 的情况几乎就是上述情况的翻版:忽略 的细节后, 的区别就在于 的位置也是必须前缀最大值,这样的位置就对应了长度为 的段。
但是,但是,当我们重新讨论 的情况时,我们发现 对应的段如果为长度为 ,则会出现相当多的例外。这是因为, 之前没有任何元素,所以删除它后 一定是前缀最大值,而 ,所以 必须是“必然前缀最大值”。于是,我们可以对于 对应长度为 的段的情况进行一个单独的计算。
将 和 合起来,就可以得到繁琐的讨论:
-
如果 对应的段长度 :
-
如果 对应的段长度为 ,且 :
-
如果 对应的段长度为 ,且 ,此时可以容斥计算:
-
如果 对应的段长度为 ,且 ,此时可以直接计算:
可以在 的时间内预处理后面 GF 的系数。直接枚举看似是 的,但是注意到 实际上不需要直接枚举,我们只需要由 确定 的个数就行,于是复杂度可以被优化到 。
顺便一说,上述分析并没有考虑 的条件。不过这并不复杂,对于 ,用上面的方式计算即可;对于 ,单独计算 的数量即可。
最后,处理 可能算重的历史遗留问题。考虑到直接计算都非常困难,我们还是倾向于证明“不会算重”。
证明思路并不复杂,用不等式导出矛盾即可,以下为详细过程:
Property.
不存在满足如下性质的 :
存在 和 的排列 ,使得 可以由 构造。
假设存在。设 。
考察 。若 位置为 中前缀最大值,则 且 一定不是 中前缀最大值。
这一点可以导出 。
设 中有 个 , 个 。容易发现,有 。
此外,任意一个 的 必然类似地满足 为 中前缀最大值, 且 一定不是 中前缀最大值。于是 ,导出 ,接着可以导出 。
最后,在 中,前缀最大值至少有 个,所以 ,于是有 。
假如 ,可以解得 。所以当 时,这样的 不存在;当 时,经过暴力验证也可以发现这样的 不存在。
于是这个恼人的问题就解决了。
代码
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构