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 a of length n. Start with c=0. Then, for each i from 1 to n (in increasing order) do exactly one of the following:

  • Option 1: set c to c+ai.
  • Option 2: set c to |c+ai|, where |x| is the absolute value of x.

Let the maximum final value of c after the procedure described above be equal to k. Find the number of unique procedures that result in c=k. Two procedures are different if at any index i, one procedure chose option 1 and another chose option 2, even if the value of c is equal for both procedures after that turn.

Since the answer may be large, output it modulo 998244353.

Input

The first line contains a single integer t (1t104) — the number of test cases.

The first line of each test case contains a single integer n (2n2105).

The second line of each test case contains n integers a1,a2,,an (109ai109).

The sum of n over all test cases does not exceed 3105.

Output

For each test case, output a single integer — the number of unique procedures that result in c=k, modulo 998244353.

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 c is 3. There are 12 ways to achieve this because in order to get 3, we have to take absolute value at indices 2 or 4, or both, resulting in 3 ways. For the other two indices, it doesn't change the value whether we take absolute value or not, so we have 22=4 ways for them. In total, we have 34=12 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 28=256 possible ways.

 

解题思路

  先考虑动态规划,定义 f(i,0/1) 表示对前 i 个数操作的所有方案中的最大值 / 最小值。直接暴力转移即可,即有:

f(i,0)=max{f(i1,0)+ai,|f(i1,0)+ai|,f(i1,1)+ai,|f(i1,1)+ai|}f(i,1)=min{f(i1,0)+ai,|f(i1,0)+ai|,f(i1,1)+ai,|f(i1,1)+ai|}

  之所以要考虑最小值,是因为 |f(i1,1)+ai| 有可能会比 f(i1,0)+ai 大。

  最后答案就是 f(n,0)

  接下来考虑如何求方案数,定义 g(i,0/1) 表示在前 i 个数中取得 f(i,0/1) 的方案数。从上面的状态转移方程可以知道,如果某个 f(i1,) 可以转移到 f(i,0/1),那么这个状态对应的 g(i1,) 就可以转移到 g(i,0/1),即

{g(i,j)g(i1,0),iff(i,0)=f(i1,0)+aig(i,j)g(i1,0),iff(i,0)=|f(i1,0)+ai|g(i,j)g(i1,1),iff(i,1)=f(i1,1)+aig(i,j)g(i1,1),iff(i,1)=|f(i1,1)+ai|j=0,1

  不过需要注意的是,如果 f(i1,0)=f(i1,1),意味着这两种情况对应的操作方案是完全一样的,此时我们应该选择其中一种情况(最大值或最小值)转移到 g(i,0/1),否则方案数就会有重复。

  最后答案就是 g(n,0)

  AC 代码如下,时间复杂度为 O(n)

#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;
}

  再给出官方题解的做法,还是很难想到的。

  事实上对于操作 2 我们至多执行一次。假设执行了至少两次操作 2,我们关注最后执行操作 2 的两个位置 ij (i<j)。此时必然有 c+ai<0c+aj<0,否则完全可以用操作 1 代替。对第 i 个位置的操作变成操作 1,那么在第 j 个位置操作前 c 会变小,又因为 c+aj<0,因此对位置 j 执行操作 2c 就会变大。因此至多执行一次操作 2

  那么对哪个位置执行操作 2 呢?定义前缀和 si=i=1nai,则应该在 x=argmini{si} 处。

  证明:在位置 x 执行操作 2 后答案就是 |sx|+snsx,又因为对于任意的 i 都有 sisx,因此

|sx|+snsx=|sx|+snsi(sxsi)|si|+snsi(sxsi)|si|+snsi

  再考虑方案数。先考虑 sx0 的情况,显然方案数应该是 2n

  否则,先考虑 i<x 的位置可以执行哪些操作,首先每一个位置都可以执行操作 1,而如果位置 i 可以执行操作 2,那么就要保证 c+ai=|c+ai|,即保证 si0(否则最终答案会改变)。假设有 c 个这样的位置。

  再考虑 i>x 的位置,对位置 x 执行操作 2 后,因为 |sx|+sisxsx+sisx=si,因此每个位置无论执行什么操作,结果都是非负的。

  因此最后的方案数是就是 i=1n[si=sx]2c+ni

  AC 代码如下,时间复杂度为 O(n)

#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

posted @   onlyblues  阅读(41)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示