C2. Magnitude (Hard Version)
C2. Magnitude (Hard Version)
The two versions of the problem are different. You may want to read both versions. You can make hacks only if both versions are solved.
You are given an array of length . Start with . Then, for each from to (in increasing order) do exactly one of the following:
- Option : set to .
- Option : set to , where is the absolute value of .
Let the maximum final value of after the procedure described above be equal to . Find the number of unique procedures that result in . Two procedures are different if at any index , one procedure chose option and another chose option , even if the value of is equal for both procedures after that turn.
Since the answer may be large, output it modulo .
Input
The first line contains a single integer () — the number of test cases.
The first line of each test case contains a single integer ().
The second line of each test case contains integers ().
The sum of over all test cases does not exceed .
Output
For each test case, output a single integer — the number of unique procedures that result in , modulo .
Example
input
5
4
2 -5 3 -3
8
1 4 3 4 1 4 3 4
3
-1 -2 -3
4
-1000000000 1000000000 1000000000 1000000000
4
1 9 8 4
output
12
256
1
8
16
Note
In the first test case, it can be shown that our maximal final value of is . There are ways to achieve this because in order to get , we have to take absolute value at indices or , or both, resulting in ways. For the other two indices, it doesn't change the value whether we take absolute value or not, so we have ways for them. In total, we have ways.
In the second test case, taking the absolute value will never change anything, so we can either take absolute value or not, for every index. This gives us possible ways.
解题思路
先考虑动态规划,定义 表示对前 个数操作的所有方案中的最大值 最小值。直接暴力转移即可,即有:
之所以要考虑最小值,是因为 有可能会比 大。
最后答案就是 。
接下来考虑如何求方案数,定义 表示在前 个数中取得 的方案数。从上面的状态转移方程可以知道,如果某个 可以转移到 ,那么这个状态对应的 就可以转移到 ,即
不过需要注意的是,如果 ,意味着这两种情况对应的操作方案是完全一样的,此时我们应该选择其中一种情况(最大值或最小值)转移到 ,否则方案数就会有重复。
最后答案就是 。
AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5, mod = 998244353;
int a[N];
LL f[N][2], g[N][2];
void solve() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", a + i);
}
g[0][0] = g[0][1] = 1;
for (int i = 1; i <= n; i++) {
f[i][0] = max({f[i - 1][0] + a[i], abs(f[i - 1][0] + a[i]), f[i - 1][1] + a[i], abs(f[i - 1][1] + a[i])});
f[i][1] = min({f[i - 1][0] + a[i], abs(f[i - 1][0] + a[i]), f[i - 1][1] + a[i], abs(f[i - 1][1] + a[i])});
for (int j = 0; j <= 1; j++) {
g[i][j] = 0;
if (f[i][j] == f[i - 1][0] + a[i]) g[i][j] = (g[i][j] + g[i - 1][0]) % mod;
if (f[i][j] == abs(f[i - 1][0] + a[i])) g[i][j] = (g[i][j] + g[i - 1][0]) % mod;
if (f[i - 1][0] == f[i - 1][1]) continue;
if (f[i][j] == f[i - 1][1] + a[i]) g[i][j] = (g[i][j] + g[i - 1][1]) % mod;
if (f[i][j] == abs(f[i - 1][1] + a[i])) g[i][j] = (g[i][j] + g[i - 1][1]) % mod;
}
}
printf("%d\n", g[n][0]);
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}
再给出官方题解的做法,还是很难想到的。
事实上对于操作 我们至多执行一次。假设执行了至少两次操作 ,我们关注最后执行操作 的两个位置 和 。此时必然有 和 ,否则完全可以用操作 代替。对第 个位置的操作变成操作 ,那么在第 个位置操作前 会变小,又因为 ,因此对位置 执行操作 后 就会变大。因此至多执行一次操作 。
那么对哪个位置执行操作 呢?定义前缀和 ,则应该在 处。
证明:在位置 执行操作 后答案就是 ,又因为对于任意的 都有 ,因此
再考虑方案数。先考虑 的情况,显然方案数应该是 。
否则,先考虑 的位置可以执行哪些操作,首先每一个位置都可以执行操作 ,而如果位置 可以执行操作 ,那么就要保证 ,即保证 (否则最终答案会改变)。假设有 个这样的位置。
再考虑 的位置,对位置 执行操作 后,因为 ,因此每个位置无论执行什么操作,结果都是非负的。
因此最后的方案数是就是 。
AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5, mod = 998244353;
LL s[N];
int p[N];
void solve() {
int n;
scanf("%d", &n);
LL mn = 1e18;
p[0] = 1;
for (int i = 1; i <= n; i++) {
scanf("%lld", s + i);
s[i] += s[i - 1];
mn = min(mn, s[i]);
p[i] = p[i - 1] * 2ll % mod;
}
if (mn >= 0) {
printf("%d\n", p[n]);
return;
}
int ret = 0;
for (int i = 1, c = 0; i <= n; i++) {
if (s[i] == mn) ret = (ret + 1ll * p[c] * p[n - i]) % mod;
if (s[i] >= 0) c++;
}
printf("%d\n", ret);
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}
参考资料
Codeforces Global Round 26 Editorial:https://codeforces.com/blog/entry/130252
Codeforces Global Round 26 A~E:https://zhuanlan.zhihu.com/p/702528746
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/18246623
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效