数列
题目描述
给定一个长度是 n n n 的数列 A A A ,我们称一个数列是完美的,当且仅当对于其任意连续子序列的和都是正的。
现在你有一个操作可以改变数列,选择一个区间 [ l , r ] [l,r] [l,r] 满足 ∑ i = l r A i < 0 \sum\limits_{i = l}^r A_i < 0 i=l∑rAi<0 ,其中 1 < l ≤ r < n 1 < l \le r < n 1<l≤r<n。
令 S = ∑ i = l r A i S = \sum\limits_{i = l}^r A_i S=i=l∑rAi ,对于 A l − 1 A_{l - 1} Al−1 和 A r + 1 A_{r + 1} Ar+1 分别加上 S S S, A l A_l Al 和 A r A_r Ar 分别减去 S S S(如果 l = r l = r l=r 就减两次)。问最少几次这样的操作使得最终数列是完美的。
对于 100 % 100\% 100% 的数据,满足 1 ≤ N ≤ 1 0 5 1 \le N \le 10^5 1≤N≤105 ; 1 ≤ ∣ A i ∣ < 2 31 1 \le |A_i| < 2^{31} 1≤∣Ai∣<231
解析:
因为有一段区间的和 S = ∑ i = x y a i S = \sum\limits_{i=x}\limits^{y} a_i S=i=x∑yai,所以可以考虑一下前缀和。
设 S = ∑ i = l r a i S = \sum\limits_{i=l}\limits^{r} a_i S=i=l∑rai, s u m sum sum 为前缀和数组。一次操作相当于是 s u m [ l − 1 ] = s u m [ l − 1 ] + S , s u m [ r ] = s u m [ r ] − S sum[l-1] = sum[l-1] + S,sum[r] = sum[r] - S sum[l−1]=sum[l−1]+S,sum[r]=sum[r]−S, 1 < l ≤ r < n 1 < l \le r < n 1<l≤r<n
注意到在操作之前有 S + s u m [ l − 1 ] = s u m [ r ] S + sum[l-1] = sum[r] S+sum[l−1]=sum[r],所以一次操作相当于是前缀和数组交换两个元素的位置。
连续子序列的和为正,等价于排序后的前缀和严格递增且
s
u
m
[
1
]
>
0
sum[1] > 0
sum[1]>0。
所以,题意可以转化为:前缀和序列,任意交换两个数,变为严格单调递增的最少操作次数。
先离散化。需要交换的元素会形成一个环,一个环的最少操作次数为环上节点个数减一。可以用并查集维护是否在一个环上。
前缀和如果有元素为负,或者出现两个相等元素,无解。特别注意,操作不可以交换最后一个数,所以开始时,前缀和最后一个位置必须最大,否则也无解
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
const int maxn = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;
int fa[maxn], siz[maxn];
void init(int x){
for(int i = 1; i <= x; i++){
siz[i] = 1;
fa[i] = i;
}
}
int find(int x){
return x == fa[x] ? fa[x] : fa[x] = find(fa[x]);
}
void merge(int x, int y){
int fx = find(x);
int fy = find(y);
if(fx != fy){
fa[fx] = fy;
siz[fy] += siz[fx];
}
}
ll a[maxn];
struct node{
ll val, id;
bool operator < (const node &b) const{
return val < b.val;
}
}b[maxn];
set<ll> s;
ll ans, n, maxsum;
int main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n;
for(int i = 1; i <= n; i++)
cin >> a[i];
for(int i = 1; i <= n; i++){
b[i].val = b[i-1].val + a[i];
b[i].id = i;
if(b[i].val < 0 || s.count(b[i].val)){
cout << -1 << endl;
return 0;
}
s.insert(b[i].val);
if(i == n && b[i].val <= maxsum){
cout << -1 << endl;
return 0;
}
maxsum = max(maxsum, b[i].val);
}
sort(b+1, b+1+n);
init(n);
for(int i = 1; i <= n; i++){
int x = b[i].id;
if(find(x) != find(i))
merge(x, i);
}
for(int i = 1; i <= n; i++){
if(find(i) == i)
ans += siz[i]-1;
}
cout << ans << endl;
return 0;
}