agc041d Problem Scores
题目大意
给出数列长度
N
N
N (
N
≤
5000
N \leq 5000
N≤5000)与质数
M
M
M (
M
≤
1
0
9
M\le 10^9
M≤109) 输出满足以下三个条件的数列的数目
∀
i
∈
[
1
,
n
)
,
a
i
≤
a
i
+
1
\forall i \in [1,n) ,a_i \leq a_{i+1}
∀i∈[1,n),ai≤ai+1
∀ i ∈ [ 1 , n ] , 1 ≤ a i ≤ n \forall i \in [1,n],1 \leq a_i \leq n ∀i∈[1,n],1≤ai≤n
∀ k ∈ [ 1 , n ) , ∑ i = 1 k + 1 a i > ∑ i = n − k + 1 n a i \forall k \in [1,n),\sum_{i=1}^{k+1} a_i > \sum_{i=n-k+1}^{n} a_i ∀k∈[1,n),i=1∑k+1ai>i=n−k+1∑nai
思路
条件一可以简述为数列单调不降
条件二可以通过条件一化简为 a 1 ≥ 1 a_1 \ge 1 a1≥1 与 a n ≤ n a_n \le n an≤n
条件三较为复杂,对其进行分析
当前 k + 1 k+1 k+1 个数与后 k k k 个数重叠时,考虑去掉重叠的数,可以转化为不重叠的情况
因此条件三只需要考虑 ∀ k ∈ [ 1 , ⌊ n − 1 2 ⌋ ] \forall k \in [1,\lfloor \frac {n-1}{2} \rfloor ] ∀k∈[1,⌊2n−1⌋] 的情况即可
观察该区间内的不等式,发现若对于 k = x k=x k=x 不等式成立,则对于 k = x − 1 k=x-1 k=x−1 不等式依旧成立
因为 k k k 从 x x x 到 x − 1 x-1 x−1 ,相当于不等号左边减掉一个较小的数,右边减掉一个较大的数,所以依旧成立
因此,如果对于 k = ⌊ n − 1 2 ⌋ k=\lfloor \frac {n-1}{2} \rfloor k=⌊2n−1⌋ 有不等式成立,则对于 ∀ k ∈ [ 1 , ⌊ n − 1 2 ⌋ ] \forall k \in [1,\lfloor \frac {n-1}{2} \rfloor ] ∀k∈[1,⌊2n−1⌋] ,不等式均成立
现给出条件四
k
=
⌊
n
−
1
2
⌋
,
∑
i
=
1
k
+
1
a
i
>
∑
i
=
n
−
k
+
1
n
a
i
k=\lfloor \frac {n-1}{2} \rfloor,\sum_{i=1}^{k+1} a_i > \sum_{i=n-k+1}^{n} a_i
k=⌊2n−1⌋,i=1∑k+1ai>i=n−k+1∑nai
综上,条件四与条件三等价
一般来说,统计方案数可以考虑按位从左到右dp,下标需要记录
另外,条件一的单调不降需要记录当前位的 a i a_i ai 的值
而条件四需要记录之前所有 a i a_i ai 的和
这样就至少需要记三维的状态了,但 n n n 有五千,显然不能直接dp
考虑通过差分去掉单调性
单调不降表示 a i + 1 ≥ a i a_{i+1}\ge a_i ai+1≥ai 也即 a i + 1 − a i ≥ 0 a_{i+1}-a_i\ge 0 ai+1−ai≥0 ,于是令 d i = a i − a i − 1 d_i=a_i-a_{i-1} di=ai−ai−1 (先将 a 0 a_0 a0 视为 0 0 0 )
发现由于 a 1 ≥ 1 a_1\ge 1 a1≥1 , d 1 d_1 d1 的限制有些另类,方便起见,还是将 a 0 a_0 a0 视为 1 1 1 吧
于是有 a i = a 0 + ∑ j = 1 i d j = 1 + ∑ j = 1 i d j a_i=a_0+\sum_{j=1}^{i}d_j=1+\sum_{j=1}^{i}d_j ai=a0+∑j=1idj=1+∑j=1idj
将上式代入条件一,二,四,并化简得到新的三个条件
∀
i
∈
[
1
,
n
]
,
d
i
≥
0
\forall i \in [1,n] , d_i \ge 0
∀i∈[1,n],di≥0
∑ i = 1 n d i ≤ n − 1 \sum_{i=1}^{n}d_i \le n-1 i=1∑ndi≤n−1
∑ j = 1 n d j c j ≤ 0 c j = { j − 2 1 ≤ j ≤ ⌊ n 2 ⌋ + 1 n − j + 1 ⌊ n 2 ⌋ + 1 < j ≤ n \sum_{j=1}^{n} d_j c_j \le 0 \\ c_j=\left\{ \begin{array}{rcl} j-2 & & {1\le j \le \lfloor \frac{n}{2}\rfloor +1}\\ n-j+1 & & {\lfloor \frac{n}{2}\rfloor +1 < j \le n}\\ \end{array} \right. j=1∑ndjcj≤0cj={j−2n−j+11≤j≤⌊2n⌋+1⌊2n⌋+1<j≤n
那么问题转化为统计合法的数列 d d d 的个数
发现条件二和条件三还是要记两个状态,不能直接做
观察 c j c_j cj,发现先递增,后递减,并且除了 c 1 = − 1 < 0 c_1=-1<0 c1=−1<0,其他的 c j c_j cj 都非负,只有第一位最特殊
于是考虑提出 d 1 d_1 d1
首先 d 1 ≥ 0 d_1 \ge 0 d1≥0 是肯定的
将条件二中的
d
1
d_1
d1 提出得
d
1
≤
n
−
1
−
∑
i
=
2
n
d
i
d_1\le n-1-\sum_{i=2}^{n} d_i
d1≤n−1−i=2∑ndi
将条件三中的
d
1
d_1
d1 提出得
d
1
≥
∑
j
=
2
n
d
j
c
j
d_1\ge \sum_{j=2}^{n} d_j c_j
d1≥j=2∑ndjcj
发现这两个条件恰好就是
d
1
d_1
d1 的上下限,于是合起来就是
0
≤
∑
j
=
2
n
d
j
c
j
≤
d
1
≤
n
−
1
−
∑
i
=
2
n
d
i
0\le \sum_{j=2}^{n} d_j c_j \le d_1 \le n-1-\sum_{i=2}^{n} d_i
0≤j=2∑ndjcj≤d1≤n−1−i=2∑ndi
所以
d
1
d_1
d1 可以取
[
∑
j
=
2
n
d
j
c
j
,
n
−
1
−
∑
j
=
2
n
d
j
]
[\sum_{j=2}^{n} d_j c_j,n-1-\sum_{j=2}^{n} d_j]
[∑j=2ndjcj,n−1−∑j=2ndj] 中的任意值
综上,当其他 d i d_i di 都确定下来后, d 1 d_1 d1 有 m a x ( n − ∑ j = 2 n d j ( 1 + c j ) , 0 ) max(n-\sum_{j=2}^{n}d_j(1+c_j),0) max(n−∑j=2ndj(1+cj),0) 种取值
然后数列就可以分成 d 1 d_1 d1 和 d 2 . . d n d_2..d_n d2..dn 两段来考虑了
考虑枚举后面那段的权值和 i = ∑ j = 2 n d j ( 1 + c j ) i=\sum_{j=2}^{n}d_j(1+c_j) i=∑j=2ndj(1+cj)
易得,先考虑后面那段的合法方案 ,再考虑 d 1 d_1 d1 的取值就可以正确地算出总方案数
(对后面那段的限制只有 d i ≥ 0 d_i \ge 0 di≥0 ,其他所有限制都在 d 1 d_1 d1 的取值里考虑进去了)
令 f i f_i fi 为 i = ∑ j = 2 n d j ( 1 + c j ) i=\sum_{j=2}^{n}d_j(1+c_j) i=∑j=2ndj(1+cj) 的方案数
于是答案为 ∑ i = 0 n f i ∗ m a x ( n − i , 0 ) = ∑ i = 0 n f i ( n − i ) \sum_{i=0}^{n}f_i*max(n-i,0)=\sum_{i=0}^{n}f_i(n-i) ∑i=0nfi∗max(n−i,0)=∑i=0nfi(n−i)
问题转化为求 f i f_i fi
令 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示后只考虑 d i . . d n d_i..d_n di..dn 的情况下, j = ∑ k = i n d k ( 1 + c k ) j=\sum_{k=i}^{n}d_k(1+c_k) j=∑k=indk(1+ck) 的方案数
于是 d p [ 2 ] [ 1.. n ] dp[2][1..n] dp[2][1..n] 即为 f [ 1 ] . . f [ n ] f[1]..f[n] f[1]..f[n]
大力转移即可,第一维可以滚动,也可以像完全背包那样优化掉
复杂度 O ( n 2 ) O(n^2) O(n2)
代码
#include <bits/stdc++.h>
using namespace std;
int dp[5005],c[5005];
int n,MOD,ans;
int main()
{
cin>>n>>MOD;
for (int i=1;i<=n;++i) if (i<=n/2+1) c[i]=i-2; else c[i]=n-i+1;
dp[0]=1;
for (int i=n;i>1;--i)
for (int j=c[i]+1;j<=n;++j)
(dp[j]+=dp[j-c[i]-1])%=MOD;
for (int i=0;i<=n;++i) ans=(1LL*dp[i]*(n-i)+ans)%MOD;
cout<<ans<<endl;
return 0;
}