1 条题解

  • 0
    @ 2026-3-15 16:23:29

    Subtask 1

    下文记 p=30p = 30。

    众所周知,2a⋅2b=2a+b2^a \cdot 2^b = 2^{a+b},因此魔法是在指数上加 aa。

    由于最终的指数和一定等于最初的指数和加上 makmak,因此题目等价于最大化:

    ∑i=1n(bi mod p)\sum _{i = 1} ^n (b_i \bmod p)

    根据直觉,对 pp 取模等于不断减去 pp 直至目标小于 pp,因此最大化上面的值,就要尽量减少减去 pp 的次数:每次取  mod  p\bmod\ p 最小的 mm 个数字加 aa,最不容易触发「减去 pp」操作。

    这一策略不能通过样例的第 6 组测试数据,但可以证明它在子任务 1 下是正确的。

    直接借助 std::sort 模拟上述过程的时间复杂度是 O(Tknlog⁡n)O(Tkn \log n),空间复杂度是 O(n)O(n),可以通过子任务 1。

    Subtask 2

    众所周知,对于题目中的方式,可以先确定每个数字被加的次数 —— 只要次数总和等于 mkmk,并且每个数字被加的次数不超过 kk,就一定存在对应方案。

    于是我们可以设 fi,jf_{i, j} 表示前 ii 个数字一共被加 jj 次后这 ii 个数  mod  p\bmod\ p 之和的最大值,初始状态为 f0,0=0f_{0, 0} = 0,转移时枚举下一个数字被加的次数即可,最终的最大总和是 fn,mkf_{n, mk}。

    该算法的时间复杂度是 O(Tnmk2)O(Tnmk^2),空间复杂度是 O(nmk)O(nmk),可以通过子任务 2。

    Subtask 3

    注意到对一个数进行 pp 次加 aa 操作不会改变它  mod  p\bmod\ p 的值。当 kk 很大时,只要确定每个数字被操作的次数  mod  p\bmod\ p 的值,使这些值的总和不大于 mkmk 且与 mkmk 在模 pp 意义下同余,就很可能存在对应的方案 —— 具体来说,在这些值的基础上,每次选择一个值加上 pp,直至总和达到 mkmk。

    上面的「很可能」要求构造方案的过程中不会出现「所有数都大于 k−pk - p,而总和还没达到 mkmk」的情况。此时这 nn 个值的总和至少为 n(k−p)n(k - p),因此我们有不等式:

    n(k−p)<mkn(k - p) < mk nk−np<mknk - np < mk nk−mk<npnk - mk < np (n−m)k<np(n - m)k < np k<p⋅nn−mk < p \cdot \dfrac{n}{n - m}

    在子任务 3 的特殊限制下,nn−m≤2\dfrac{n}{n - m} \le 2,因此 k≥2pk \ge 2p 时就可以放心使用以上结论:只确定每个数字被操作的次数  mod  p\bmod\ p 的值,按照子任务 2 中的 DP 算法求解。

    上述 DP 算法的时间复杂度为 O(Tn2p2)O(Tn^2p^2);当 k<2pk < 2p 时需要回退到子任务 2 的解法,时间复杂度为 O(Tnmk2)O(Tnmk^2),在 k<2pk < 2p 的限制下同样不超过 O(Tn2p2)O(Tn^2p^2)。

    因此该算法的时间复杂度是 O(Tn2p2)O(Tn^2p^2),空间复杂度是 O(n2p)O(n^2p),可以通过子任务 3。

    Subtask 4

    当 2m>n2m > n 时,我们可以先把所有数字加上 akak,然后操作变成了「施展 kk 次魔法,每次魔法选择 n−mn - m 个数字加上 −a-a」。此时我们有 2(n−m)<n2(n - m) < n,并且前面所有解法的正确性都不依赖 aa 的任何性质,因此我们可以回退到子任务 3 的解法。

    最终的时间复杂度是 O(Tn2p2)O(Tn^2p^2),空间复杂度是 O(n2p)O(n^2p),可以通过子任务 4。

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 30, P = 30, M = N + 12, Q = 2 * N * P + 12;
    int b[M], dp[M][Q];
    int main()
    {
    	freopen("python.in", "r", stdin);
    	freopen("python.out", "w", stdout);
    	int T;
    	cin >> T;
    	while (T--)
    	{
    		memset(dp, 0xc0, sizeof dp);
    		int n, m, k, a, c, ans = 0;
    		cin >> n >> m >> k >> a;
    		__int128 sum = (__int128) m * (__int128) a * (__int128) k;
    		if (k >= 2 * P) c = P - 1;
    		if (k <= 2 * P) c = k;
    		a %= P;
    		for (int i = 1; i <= n; i++)
    		{
    			cin >> b[i];
    			sum += b[i];
    			b[i] %= P;
    		}
    		if (2 * m > n)
    		{
    			for (int i = 1; i <= n; i++)
    				b[i] = (b[i] + 1ll * a * k) % P;
    			m = n - m;
    			a = (P - a) % P;
    		}
    		dp[0][0] = 0;
    		for (int i1 = 1; i1 <= n; i1++)
    			for (int i2 = 0; i2 <= min(i1 * c, m * min(k, n * P)); i2++)
    				for (int i3 = 0; i3 <= min(i2, c); i3++)
    					dp[i1][i2] = max(dp[i1][i2], dp[i1 - 1][i2 - i3] + (b[i1] + a * i3) % P);
    		if (k > 2 * P)
    			for (int i = 1ll * m * k % P; i <= min(1ll * n * c, 1ll * m * k); i += P)
    				ans = max(ans, dp[n][i]);
    		if (k <= 2 * P) ans = dp[n][m * k];
    		cout << (long long) (4 * ((sum - ans) / P) + 36 * n + 56) << endl;
    	}
    	return 0;
    }
    
    • 1

    信息

    ID
    233
    时间
    1000ms
    内存
    512MiB
    难度
    8
    标签
    递交数
    71
    已通过
    7
    上传者