AT5695「AGC041D」Problem Scores

本文探讨了一种基于组合数学的优化算法,通过设定特定条件下的数列分配,利用动态规划求解最优解。针对不同数量级的输入,算法采用不同的策略确保高效运行。文章详细解析了算法的设计思路、实现细节及时间复杂度分析。
摘要由CSDN通过智能技术生成

Address

Solution

  • 尝试简化第三个限制,设 k = ⌊ n − 1 2 ⌋ k = \lfloor \dfrac{n - 1}{2}\rfloor k=2n1,我们取前 k + 1 k + 1 k+1 个数作为第一个集合,取后 k k k 个数作为第二个集合,因为这两个集合的差是所有选集合的方案中最小的,所以只要这两个集合满足条件,所有的集合都能满足条件。

Algorithm 1

  • 因为 n n n 的奇偶性会影响中间是否会多出一个数,我们分情况讨论:

    1. n n n 是偶数,枚举第 k + 2 k + 2 k+2 个数为 x x x,那么对剩下的数有如下要求:

      1. x x x 与前 k + 1 k + 1 k+1 个数的差 ∈ [ 0 , x ) \in [0,x) [0,x) 且差递减;
      2. k k k 个数与 x x x 的差 ∈ [ 0 , n − x ] \in [0,n-x] [0,nx] 且差递增;
      3. x x x 与前 k + 1 k + 1 k+1 个数的差的和加上后 k k k 个数与 x x x 的差的和小于 x x x

      f i , j f_{i,j} fi,j 表示已经确定了前 i i i 个数、 x x x 与这些数的差的和为 j j j 的方案数。

      转移即从大到小枚举差 l ( l ∈ [ 0 , n ) ) l(l \in [0,n)) l(l[0,n)),令 f i + 1 , j + l = f i + 1 , j + l + f i , j f_{i + 1,j + l} = f_{i + 1,j + l} + f_{i,j} fi+1,j+l=fi+1,j+l+fi,j

      由于第二维 j ≤ n j \le n jn,存在合法方案时一定有 i l ≤ n il \le n iln,时间复杂度 O ( n 2 ln ⁡ n ) \mathcal O(n^2 \ln n) O(n2lnn)

      k k k 个数的方案可以看做是选取若干个后缀区间 +1,设 g i , j g_{i,j} gi,j 表示已经选了 i i i 个后缀、这些后缀的长度之和为 j j j 的方案数。

      转移即从大到小枚举后缀长度 l ( l ∈ [ 0 , k ] ) l(l \in [0,k]) l(l[0,k]),令 g i + 1 , j + l = g i + 1 , j + l + g i , j g_{i + 1,j + l} = g_{i + 1,j + l} + g_{i,j} gi+1,j+l=gi+1,j+l+gi,j,时间复杂度同样是 O ( n 2 ln ⁡ n ) \mathcal O(n^2 \ln n) O(n2lnn)

      最后的答案即为:
      ∑ x = 1 n ∑ i = 0 x − 1 f k + 1 , i ∑ j = 0 x − i − 1 g n − x , j \begin{aligned}\sum \limits_{x = 1}^{n} \sum \limits_{i = 0}^{x - 1} f_{k + 1,i} \sum \limits_{j = 0}^{x - i - 1}g_{n-x,j}\end{aligned} x=1ni=0x1fk+1,ij=0xi1gnx,j
      预处理 g g g 第二维的前缀和即可 O ( n 2 ) \mathcal O(n^2) O(n2) 计算。

    2. n n n 是奇数,枚举第 k + 1 k + 1 k+1 个数为 x x x,只要最后算答案时把 f k + 1 , i f_{k+1,i} fk+1,i 改为 f k , i f_{k,i} fk,i,其它部分与偶数的情况完全相同。

  • 总的时间复杂度 O ( n 2 ln ⁡ n ) \mathcal O(n^2 \ln n) O(n2lnn)

Code 1

#include <bits/stdc++.h>

const int N = 5005;
int f[N][N], g[N][N];
int n, ans, mod;

inline void add(int &x, int y)
{
	x += y;
	x >= mod ? x -= mod : 0;
}

int main()
{
	scanf("%d%d", &n, &mod);	
	int k = n - 1 >> 1;
	f[0][0] = g[0][0] = 1;
	for (int c = n - 1; c >= 0; --c)
		for (int i = 0, im = !c ? n : n / c; i <= im; ++i)
			for (int j = 0; j <= n - c; ++j)
				add(f[i + 1][j + c], f[i][j]);
	for (int c = k; c >= 0; --c)
		for (int i = 0, im = !c ? n : n / c; i <= im; ++i)
			for (int j = 0; j <= n - c; ++j)
				add(g[i + 1][j + c], g[i][j]);
	for (int i = 0; i <= n; ++i)
		for (int j = 1; j <= n; ++j)
			add(g[i][j], g[i][j - 1]);
	if (!(n & 1))
	{
		for (int x = 1; x <= n; ++x)
			for (int j = 0; j < x; ++j)
				ans = (1ll * f[k + 1][j] * g[n - x][x - j - 1] + ans) % mod;
	}
	else
	{
		for (int x = 1; x <= n; ++x)
			for (int j = 0; j < x; ++j)
				ans = (1ll * f[k][j] * g[n - x][x - j - 1] + ans) % mod;
	} 
	printf("%d\n", ans);
	return 0;
}

Algorithm 2

  • 考虑将 A A A 数组差分,去掉单调性的限制,令差分数组为 B B B

  • B 0 = 1 B_0 = 1 B0=1,则 A i = 1 + ∑ j = 1 i B j A_i = 1 + \sum \limits_{j = 1}^{i}B_j Ai=1+j=1iBj,因此其中一个限制条件就是 ∑ i = 1 n B i < n \sum \limits_{i = 1}^{n} B_i < n i=1nBi<n

  • 同样对 n n n 的奇偶性进行讨论:

    1. n n n 为奇数, ⌊ n − 1 2 ⌋ + 1 = n + 1 2 \lfloor \dfrac{n - 1}{2} \rfloor + 1 = \dfrac{n + 1}{2} 2n1+1=2n+1,列出限制条件:
      ∑ i = 1 n + 1 2 A i > ∑ i = n + 1 2 + 1 n A i ∑ i = 1 n + 1 2 ( 1 + ∑ j = 1 i B j ) > ∑ i = n + 1 2 + 1 n ( 1 + ∑ j = 1 i B j ) 2 ∑ i = 1 n + 1 2 ∑ j = 1 i B j ≥ ∑ i = 1 n ∑ j = 1 i B j 2 ∑ i = 1 n + 1 2 ( n + 1 2 − i + 1 ) B i ≥ ∑ i = 1 n ( n − i + 1 ) B i \begin{aligned}\sum \limits_{i = 1}^{\frac{n + 1}{2}}A_i &> \sum \limits_{i = \frac{n + 1}{2} + 1}^{n} A_i \\\sum \limits_{i = 1}^{\frac{n + 1}{2}}(1 + \sum \limits_{j = 1}^{i}B_j) &> \sum \limits_{i = \frac{n + 1}{2} + 1}^{n}(1 + \sum \limits_{j = 1}^{i}B_j) \\2\sum \limits_{i = 1}^{\frac{n + 1}{2}}\sum \limits_{j = 1}^{i}B_j &\ge \sum \limits_{i = 1}^{n}\sum \limits_{j = 1}^{i}B_j \\2\sum \limits_{i = 1}^{\frac{n + 1}{2}}(\dfrac{n + 1}{2} - i + 1)B_i &\ge \sum \limits_{i = 1}^{n}(n - i + 1)B_i\\\end{aligned} i=12n+1Aii=12n+1(1+j=1iBj)2i=12n+1j=1iBj2i=12n+1(2n+1i+1)Bi>i=2n+1+1nAi>i=2n+1+1n(1+j=1iBj)i=1nj=1iBji=1n(ni+1)Bi
      简单移项后,我们可以得到:
      ∑ i = 1 n C i B i ≤ 0 \begin{aligned} \sum \limits_{i = 1}^{n}C_iB_i \le 0 \end{aligned} i=1nCiBi0
      其中:
      C i = { i − 2 1 ≤ i ≤ n + 1 2 n − i + 1 n + 1 2 < i ≤ n C_i = \begin{cases}i - 2 &1 \le i \le \dfrac{n + 1}{2} \\n - i + 1 &\dfrac{n + 1}{2} < i \le n\\\end{cases} Ci=i2ni+11i2n+12n+1<in

    2. n n n 为偶数,同样可以推出 C i C_i Ci 的表达式:
      C i = { i − 2 1 ≤ i ≤ n 2 + 1 n − i + 1 n 2 + 1 < i ≤ n C_i = \begin{cases}i - 2 &1\le i \le \dfrac{n}{2} + 1\\n - i + 1 &\dfrac{n}{2} + 1 < i \le n\\\end{cases} Ci=i2ni+11i2n+12n+1<in
      整理一下,我们有:
      C i = { i − 2 1 ≤ i ≤ ⌊ n 2 ⌋ + 1 n − i + 1 ⌊ n 2 ⌋ + 1 ≤ i ≤ n C_i = \begin{cases}i - 2 &1\le i \le \lfloor \dfrac{n}{2} \rfloor + 1\\n - i + 1 &\lfloor \dfrac{n}{2} \rfloor + 1 \le i \le n\\\end{cases} Ci=i2ni+11i2n+12n+1in
      注意到只有 C 1 C_1 C1 是负数,考虑列出所有和 B 1 B_1 B1 有关的限制条件:
      { B 1 ≤ n − 1 − ∑ i = 2 n B i B 1 ≥ ∑ i = 2 n C i B i \begin{cases}B_1 \le n - 1 - \sum \limits_{i = 2}^{n}B_i \\B_1 \ge \sum \limits_{i = 2}^{n}C_i B_i \\ \end{cases} B1n1i=2nBiB1i=2nCiBi
      在已知 B i ( 2 ≤ i ≤ n ) B_i(2 \le i \le n) Bi(2in) 的情况下,合法的 B 1 B_1 B1 的个数为:
      max ⁡ { 0 , n − ∑ i = 2 n ( C i + 1 ) B i } \begin{aligned}\max\{0, n - \sum \limits_{i = 2}^{n}(C_i + 1)B_i\}\end{aligned} max{0,ni=2n(Ci+1)Bi}

  • f j f_j fj 表示 ∑ i = 2 n ( C i + 1 ) B i = j \sum \limits_{i = 2}^{n}(C_i + 1)B_i = j i=2n(Ci+1)Bi=j 的方案数,最后的答案就为 ∑ i = 0 n − 1 ( n − j ) f j \sum \limits_{i = 0}^{n - 1}(n - j)f_j i=0n1(nj)fj

  • 显然 f j f_j fj 的转移是一个完全背包,时间复杂度 O ( n 2 ) \mathcal O(n^2) O(n2)

Code 2

#include <bits/stdc++.h>

const int N = 1e4 + 5;
int n, mod, ans, c[N], f[N];

inline void add(int &x, int y)
{
	x += y;
	x >= mod ? x -= mod : 0;
}

int main()
{
	scanf("%d%d", &n, &mod);
	int half = n >> 1;
	for (int i = 2; i <= half + 1; ++i)
		c[i] = i - 1;
	for (int i = half + 2; i <= n; ++i)
		c[i] = n - i + 2;

	f[0] = 1;
	for (int i = 2; i <= n; ++i)
		for (int j = c[i]; j < n; ++j)
			add(f[j], f[j - c[i]]);
	for (int j = 0; j < n; ++j)
		ans = (1ll * (n - j) * f[j] + ans) % mod;
	printf("%d\n", ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值