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

浙公网安备 33010602011771号