1. 题面梗概
中译中:构造一个长度为 N 的非降序列 S,要求存在一个长度为 N+1 的辅助序列 A(元素均在 [0,M] 之间),满足相邻项之和等于 S 的项。求满足条件的 S 的数量。
Si=Ai+Ai+1.
1≤N,M≤107, 结果对 109+7 取模。
2. 逻辑推导
这显然是个 组合数学 推导题,在没有别的性质前,我们先从 S 的定义出发分析。
2.1 奇偶分组
因为 S 是非降的,我们试试差分能不能发现什么规律。
Di=Si+1−Si=(Ai+1+Ai+2)−(Ai+Ai+1)=Ai+2−Ai≥0
这意味着序列 A 在 奇数下标 和 偶数下标 上分别是 非降 的。
2.2 约束
因为 S1=A1+A2,那么只需要知道 S 和剩下 N−1 个 差分变量 Di 就可以构造出整个数列 S 了。
-
奇数:共有 kx=⌊2N⌋ 个变量(即 d1,d3,… ),设它们总和为 X。
-
偶数:共有 ky=⌊2N−1⌋ 个变量(即 d2,d4,… ),设它们总和为 Y。
为了保证 A 的所有元素都在 [0,M],我们分析奇偶数:
- 奇数:0≤A1≤Akx=A1+X≤M,也就是 X≤M。
- 偶数:A2+Y≤M,同理 Y≤M。
- 同时因为 A1+A2=S1,所以 S1+X+Y≤2M。
2.3 计数
现在问题就转化为了,有多少组非负整数解满足上述三个约束条件。
发现这个不等式 S1+X+Y≤2M 有点麻烦,我们把它变成等式:
S1+X+Y+W=2M
一共有 1+kx+ky+1=1+(N−1)+1=N+1 个变量。
那么把 2M 分给 N+1 个变量,根据隔板法公式:
Total=(N2M+N)
2.4 容斥
由于总数只有 2M,而条件是 X≤M 且 Y≤M,所以要么 kx 多了,要么 ky 多了。
因为 X+Y≤2M,所以它们不可能同时多拿。
假设 kx 拿多了的情况,剩下来的 统称为 Z,共 m=N+1−kx 个。
我们枚举 X 拿了 j 个的情况。( M+1≤j≤2M )
S(kx,m)=j=M+1∑2M[((kx−1)j+(kx−1))×(m−1(2M−j)+(m−1))]
X 组:kx 分 j;Z 组:m 分 2M−j。
而 ky 多拿的情况对称,只需要把 kx 换成 ky 即可。
3. 代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| #include <bits/stdc++.h> using namespace std; using ll = long long;
const int maxn = 3e7 + 5; const int mod = 1e9 + 7;
ll N, M;
ll fac[maxn], inv[maxn];
ll qpow( ll a, ll b ) { ll res = 1; while( b ) { if( b & 1 ) res = res * a % mod; a = a * a % mod; b >>= 1; } return res; }
ll C( ll n, ll k ) { if( n < k ) return 0; return ( fac[n] * inv[k] % mod ) * inv[n - k] % mod; }
void init( ) { fac[0] = inv[0] = 1; for( int i = 1; i < maxn; ++ i ) fac[i] = ( fac[i - 1] * i ) % mod; inv[maxn - 1] = qpow( fac[maxn - 1], mod - 2 ); for( int i = maxn - 2; i >= 1; -- i ) inv[i] = ( inv[i + 1] * (i + 1) ) % mod; }
ll calc( ll x, ll y ) { ll res = 0; for( int i = M + 1; i <= 2 * M; ++ i ) { ll t1 = C( i + (x - 1), (x - 1) ); ll t2 = C( (2 * M - i) + (y + 1), (y + 1) ); res = ( res + t1 * t2 ) % mod; } return res; }
void solve( ) { cin >> N >> M;
int cnt1 = N / 2, cnt2 = ( N + 1 ) / 2 - 1;
ll ans = C( 2 * M + N, N );
ans = ( ans - calc( cnt1, cnt2 ) + mod ) % mod; ans = ( ans - calc( cnt2, cnt1 ) + mod ) % mod;
cout << ans << '\n'; }
int main( ) { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); init( ); solve( ); return 0; }
|
4. 复盘