题目
题目描述
给定一个长度是
n
n
n的数列
A
A
A,我们称一个数列是完美的,当且仅当对于其任意连续子序列的和都是正的。现在你有一个操作可以改变数列,选择一个区间[X,Y]满足
A
x
+
A
x
+
1
+
⋯
+
A
Y
,
1
<
X
≤
Y
<
n
A_x +A_{x+1} + \cdots + A_Y, 1<X \leq Y<n
Ax+Ax+1+⋯+AY,1<X≤Y<n,令
S
=
A
x
+
A
x
+
1
+
…
+
A
Y
S=A_x +A_{x+1} +…+ A_Y
S=Ax+Ax+1+…+AY,对于
A
x
−
1
A_{x-1}
Ax−1和
A
Y
+
1
A_{Y+1}
AY+1分别加上
S
S
S,
A
x
A_x
Ax和
A
Y
A_Y
AY分别减去
S
S
S(如果
X
=
Y
X=Y
X=Y就减两次)。问最少几次这样的操作使得最终数列是完美的。
输入格式
第一行一个数
n
n
n,以下
n
n
n个数。
【数据规模】
对于 20 20 20%的数据,满足 1 ≤ N ≤ 5 1 \leq N \leq 5 1≤N≤5;
对于 100 100 100%的数据,满足 1 ≤ N ≤ 1 0 5 ; 1 ≤ ∣ A [ i ] ∣ ≤ 2 31 − 1 1≤N≤10^5; 1≤|A[i] |≤2^{31}-1 1≤N≤105;1≤∣A[i]∣≤231−1。
输出格式
一个数表示最少的操作次数,如果无解输出
−
1
-1
−1。
输入输出样例
输入
5
13
-3
-4
-5
62
输出
2
说明/提示
【样例解释】
首先选择区间 [ 2 , 4 ] [2,4] [2,4],之后数列变成 1 , 9 , − 4 , 7 , 50 1,9,-4,7,50 1,9,−4,7,50,然后选择 [ 3 , 3 ] [3,3] [3,3],数列变成 1 , 5 , 4 , 3 , 50 1,5,4,3,50 1,5,4,3,50
题解
- 令 S = ∑ i = x y a [ i ] , s u m [ i ] = ∑ k = 1 i S=\sum_{i=x}^ya[i], sum[i] =\sum_{k=1}^i S=∑i=xya[i],sum[i]=∑k=1i,我们可以发现每次操作只会影响前缀和,即 s u m [ x − 1 ] + = S , s u m [ x ] + = 0 , s u m [ y ] − = S , s u m [ y + 1 ] + = 0 sum[x-1]+=S, sum[x]+=0,sum[y]-=S,sum[y+1]+=0 sum[x−1]+=S,sum[x]+=0,sum[y]−=S,sum[y+1]+=0,实际上最终影响的只有 s u m [ x − 1 ] , s u m [ y ] sum[x-1],sum[y] sum[x−1],sum[y]而因为原本的 s u m [ x − 1 ] + S = s u m [ y ] sum[x-1]+S=sum[y] sum[x−1]+S=sum[y]所以其实就相当于交换 s u m [ x − 1 ] , s u m [ y ] sum[x-1],sum[y] sum[x−1],sum[y]。
- 题目转化为给定一个序列每次操作可交换序列中任意两个数,求使得序列单调递增的最小交换次数。
- O ( n ) O(n) O(n) 扫一遍即可,最终复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)
code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 100;
int n, m;
int a[N], b[N];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
int x; scanf("%d", &x);
a[i] = a[i - 1] + x;
b[i] = a[i];
}
sort(b + 1, b + n + 1);
m = unique(b + 1, b + n + 1) - b - 1;
if (b[1] <= 0) {
puts("-1");
return 0;
}
else {
int ans = 0;
for (int i = 1; i <= n; ++i) {
int t = lower_bound(b + 1, b + m + 1, a[i]) - b;
a[i] = t;
}
for (int i = 1; i < n; ++i) {
while (a[i] != i) {
++ans;
swap(a[i], a[a[i]]);
}
}
printf("%d\n", ans);
}
return 0;
}