LuoguP1667 数列

0x01

考察前缀和数组 \(pre_i = A_1 + A_2 + \ldots + A_i\).

注意到题目要求的“任意连续子序列的和都是正的”等价于 \(pre\) 数组严格单调递增,且 \(\min_{1 \le i \le n} \{ pre_i \} > 0\).

又注意到一次操作等价于交换 \(pre_{l-1}\)\(pre_r\) 的值.

则我们只需求出通过交换 \(pre\) 数组中元素使其单调递增的最小操作次数.

0x02

考虑将 \(pre\) 数组升序排序,即可得到 \(pre_i\) 变换后的位置.

我们只需求出置换群的个数 \(num\),则有 \(\text{ans} = n - num\).

0x03

然而,此题存在无解的情况.

0x01,若存在 \(pre_i \le 0\),则无解.

不难发现,若存在 \(1 \le i < j \le n\) 使得 \(pre_i = pre_j\),则无论如何排列 \(pre\) 数组,都会存在一段子区间的和为 \(0\).

且注意到题中 \(1 < l \le r < n\),即 \(pre_n\) 的值无法被交换,则必须满足 \(pre_n\) 在所有 \(pre\) 中最大,否则无解.

0x04 Code

#include <bits/stdc++.h>
#define int long long
using namespace std;

inline int read(){
    int x = 0, f = 1; char c = getchar();
    while(isdigit(c)^1) f &= (c!=45), c = getchar();
    while(isdigit(c)) x = (x<<1) + (x<<3) + (c^48), c = getchar();
    return f ? x : -x;
}

const int maxn = 100005;
int n, num, a[maxn], to[maxn], vst[maxn];

signed main(){
#ifndef ONLINE_JUDGE
	freopen("test.in", "r", stdin);
#endif
    n = read();
    for(int i=1; i<=n; ++i) a[i] = a[i-1] + read(), to[i] = i;
    sort(to+1, to+n+1, [](const int &x, const int &y){
        return a[x] < a[y];
    });
    if(to[n]!=n || a[to[1]]<0) puts("-1"), exit(0);
    for(int i=2; i<=n; ++i)
        if(a[to[i]]==a[to[i-1]]) puts("-1"), exit(0);
    for(int i=1; i<=n; num+=(!vst[i++]))
        if(!vst[i]) for(int j=to[i]; j!=i; j=to[j]) vst[j] = 1;
    printf("%lld\n", n-num);
    return 0;
}
posted @ 2022-04-01 13:35  johnsmith0x3f  阅读(41)  评论(1)    收藏  举报