📌  相关文章
📜  计算可以通过添加前 N 个自然数的任意排列来最大化的数组元素(1)

📅  最后修改于: 2023-12-03 15:41:38.369000             🧑  作者: Mango

通过添加前 N 个自然数的任意排列来最大化的数组元素

问题描述

给定一个数字 $N$,构造一个长度为 $N$ 的数组 $A$。在构造过程中,可以将前 $i$ 个自然数的任意排列添加到数组 $A$ 的前 $i$ 个元素中。数组 $A$ 中的一个元素可以被多次构造时添加的数字覆盖。构造结束后,求得数组 $A$ 的最大元素。

解决方法

此问题可以通过数学方法求解。

考虑整个构造过程,第 $i$ 个元素的值由前 $i-1$ 个元素的值和 $i$ 的排列方式决定。设第 $i$ 个元素的排列方式为 $p_i$,即:

$$A[i] = p_i(A[1], A[2], ..., A[i-1], i)$$

其中 $p_i$ 是一个将前 $i-1$ 个元素排列成一个序列后,再加上 $i$ 转换成一个长度为 $i$ 的新排列的函数。显然,$p_i$ 是一个 $i!$ 种不同排列的函数。

对于 $p_i$ 中的第 $j$ 种排列,记为 $p_i^j$。则第 $i$ 个元素的值为:

$$A[i] = p_i^j(A[1], A[2], ..., A[i-1], i)$$

当 $1\leq j \leq i!$ 时,对于任何 $1\leq k \leq i$,$A[k]$ 可以表示为:

$$A[k] = p_k^l(A[1], A[2], ..., A[k-1], k)$$

其中 $l$ 为一个整数。

现在我们需要最大化 $A[i]$,即最大化 $p_i^j(A[1], A[2], ..., A[i-1], i)$。将 $A[i]$ 替换成它的定义式,我们得到以下等式:

$$p_i^j(A[1], A[2], ..., A[i-1], i) = p_i^j(p_{i-1}^{l_1}(A[1], A[2], ..., A[i-2], i-1), p_{i-1}^{l_2}(A[1], A[2], ..., A[i-2], i-1), ..., p_{i-1}^{l_{i-1}}(A[1], A[2], ..., A[i-2], i-1), i)$$

其中 $l_1, l_2, ..., l_{i-1}$ 为整数。

我们可以将 $p_i^j(A[1], A[2], ..., A[i-1], i)$ 写成一个多项式形式,将 $p_{i-1}^{l_k}(A[1], A[2], ..., A[i-2], i-1)$ 代入其中,得到一个关于 $l_1, l_2, ..., l_{i-1}$ 的多项式 $f_j(l_1, l_2, ..., l_{i-1})$。我们需要找到 $f_j(l_1, l_2, ..., l_{i-1})$ 的最大值。

$f_j$ 的系数由 $p_i^j$ 决定,因此是固定的。可以使用二次剩余定理求出 $f_j$ 的最大值及对应的 $l_1, l_2, ..., l_{i-1}$。

然后,我们不断地对当前已经构造的数组求解最大元素,直到构造出完整的数组为止。

下面是一段 Python 代码实现此算法。

def max_value_of_array(n):
    # 定义多项式系数
    coeff = [[] for i in range(n+1)]
    for i in range(1, n+1):
        for j in range(1, i+1):
            if i == j:
                coeff[i].append(1)
            else:
                c = 0
                for k in range(len(coeff[i-1])):
                    if j > len(coeff[i-1][k]):
                        c += 0
                    else:
                        c += coeff[i-1][k][j-1]
                coeff[i].append(c)

    # 定义二次剩余函数
    def qrt(a, p):
        return pow(a, (p+1)//4, p)

    # 求解多项式最大值
    def find_max_value(p, mod):
        s = 0
        for i in range(len(p)):
            if p[i]:
                s += 1
                if s == 1:
                    a = -p[i]
                elif s == 2:
                    b = -2 * p[i]
                elif s == 3:
                    c = -p[i]
                else:
                    break
        D = b * b - 4 * a * c
        l1 = (qrt(D, mod) - b) * pow(2*a, mod-2, mod) % mod
        l2 = (-qrt(D, mod) - b) * pow(2*a, mod-2, mod) % mod
        vals = []
        for l in [l1, l2]:
            if not ((b + 2 * a * l) % mod) and l < mod:
                vals.append(l)
        return max([(p[i] * vals[i-1] % mod) for i in range(1, len(vals)+1)])

    # 计算 A 数组
    mod = 998244353
    A = [0] * (n+1)
    for i in range(1, n+1):
        p = [0]
        for j in range(1, i+1):
            # 计算多项式系数
            c = coeff[i-j][:]
            for k in range(len(c)):
                c[k] = c[k] * pow(j, k, mod) % mod
            # 计算一个排列的贡献
            p_j = find_max_value(c, mod)
            p.append(p_j)
        A[i] = max(p)
    return A[n]

调用 max_value_of_array(n) 可以得到构造出的数组 $A$ 的最大元素。