「AGC041D」Problem Scores

$Link$

求满足下列条件的长度为 $n$,值域为 $[1,n]$ 的序列 $a$ 的个数,对 $m$ 取模。

  • $\forall i\in[1,n-1],a_i\le a_{i+1}$
  • $\forall k\in[1,n-1],a$ 中任意 $k+1$ 个数的和必定大于任意 $k$ 个数的和

$2\le n\le5000,9\times10^8<m<10^9$,$m$ 为质数。

由于 $a$ 单调不降,根据常见套路,我们将 $a$ 差分,设差分数组为 $d$,其中 $d_0=1$。

那么条件 $1$ 即为 $\forall i\in[1,n],d_i\ge 0,\sum_{i=1}^n d_i\le n-1$。

对于条件 $2$,显然取最小的 $k+1$ 个数和最大的 $k$ 个数,稍加思考可知,只要满足 $k=\lfloor\frac{n-1}{2}\rfloor$ 的情况即可。转化为 $\sum_{i=1}^n c_{1_i}d_i>0$。其中 $c_{1_i}$ 为系数,$c_1=[1,0,-1,-2,\cdots,-2,-1]$。

如果我们确定了 $d_{2\cdots n}$,通过以上两个条件,即可确定 $d_1$ 的取值范围。

条件 $1$:$d_1\le n-1-\sum_{i=2}^n d_i$。

条件 $2$:$d_1>\sum_{i=2}^n c_{2_i}d_i$,$c_2=[0,1,2,\cdots,2,1]$。

而 $d_1$ 原本有 $0\sim n-1$ 共 $n$ 个取值,受到两个条件的限制,就只有 $n-\sum_{i=2}^n c_{3_i}d_i$ 个取值,$c_3=[1,2,3,\cdots,3,2]$。也就是只要求出后面的 $\sum$ 取到每个小于 $n$ 的值的方案数即可。

而这实际上就是一个完全背包。

时间复杂度 $O(n^2)$。

$code$

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
#include <iostream>
#include <cstdlib>
#include <cstdio>
using namespace std;
inline int read()
{
int f = 1, x = 0;
char ch;

do{
ch = getchar();
if (ch == '-')
f = -1;
}while(ch < '0' || ch > '9');
do{
x = x * 10 + ch - '0';
ch = getchar();
}while(ch >= '0' && ch <= '9');
return f * x;
}
const int N = 5e3;

int n, mod;
int a[N + 1];
int f[N + 1], ans;

inline void dp()
{
f[0] = 1;
for (int i = 2; i <= n; i++) {
for (int j = 1; j <= n; j++)
if (j - a[i] >= 0)
f[j] = (f[j] + f[j - a[i]]) % mod;
}
}
int main()
{
n = read();
mod = read();
a[2] = 1;
for (int i = 3, j = n; i <= j; i++, j--)
a[i] = a[j] = a[i - 1] + 1;

dp();

for (int i = 0; i <= n; i++)
ans = (ans + 1ll * (n - i) * f[i] % mod) % mod;
printf("%d\n", ans);
return 0;
}