题目描述
给定一个长度是n的数列A,我们称一个数列是完美的,当且仅当对于其任意连续子序列的和都是正的。现在你有一个操作可以改变数列,选择一个区间[X,Y]满足Ax +Ax+1 +…+ AY<0,1<X<=Y<n,令S=Ax +Ax+1 +…+ AY,对于Ax-1和AY+1分别加上S,Ax和AY分别减去S(如果X=Y就减两次)。问最少几次这样的操作使得最终数列是完美的。
输入输出格式
输入格式:
第一行一个数n,以下n个数。
【数据规模】
对于20%的数据,满足1≤N≤5;
对于100%的数据,满足1≤N≤10^5; 1≤|A[i]|≤2^31-1。
输出格式:
一个数表示最少的操作次数,如果无解输出-1。
输入输出样例
输入样例#1: 复制
5
13
-3
-4
-5
62
输出样例#1: 复制
2
说明
【样例解释】
首先选择区间[2,4],之后数列变成1,9,-4,7,50,然后选择[3,3],数列变成1,5,4,3,50
题解:
这个题目看上去很难下手。。。
但是,我们不妨对操作表示一下。假设Σ(i=x,y)a[i]=ss,则:
+ss -ss..... -ss +ss
Ax-1,Ax,……,Ay,Ay+1
假设1~i的前缀和为s[i],则:s[x-1]+=ss,s[x]不变,s[y]-=ss,s[y+1]不变,而s[x-1]+ss=s[y],不难发现,操作以后,相当于仅仅交换了s[x-1]和s[y]!而题目中要使所有连续子段和都大于0,说明最后要使前缀和递增!我们发现,无论怎么操作,前缀和的值是不会改变的,只是顺序变换了。所以,如果某一个前缀和不为正,那么最后肯定无解。或者,如果原序列某两个前缀和相等(比如s[u]和s[v](u<v)),那么说明u+1到这段和为0,不符合题意(最后无论怎么变还是会有这样的情况出现)。而我们就是要求使得前缀和递增的最小交换次数。
下面是AC代码
//#include<bits/stdc++.h>
#include <unordered_map>
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<set>
#include<climits>
#include<queue>
#include<cmath>
#include<stack>
#include<map>
using namespace std;
#define ll long long
#define MT(a,b) memset(a,b,sizeof(a))
const int INF = 0x3f3f3f3f;
const int ONF = -0x3f3f3f3f;
const int mod = 998244353;
const int maxn = 2e4+5;
const int N = 1e5+5;
const double PI = 3.141592653589;
const double E = 2.718281828459;
struct dd{
ll sum,id; //记录第i个数的前缀和,和位置(为了离散化)
bool friend operator < (dd x, dd y){
return x.sum<y.sum;
}
};
int main()
{
int n; scanf("%d",&n);
dd a[N] ; MT(a, 0);
for(int i=1 ;i<= n;i++)
{
ll num ;scanf("%lld",&num);
a[i] = { num+a[i-1].sum , i};
}
sort(a+1 ,a+n+1);
for(int i=1;i<n;i++) if(a[i].sum==a[i+1].sum || a[i].sum<0)
{
printf("-1");
return 0;
} //判断是否满足条件
int p[N] ; MT(p,0);
for(int i=1;i<=n;i++) p[a[i].id] = i; //离散化
unordered_map<int ,int >mp; //也可以直接用map只是map更慢
for(int i=1;i<=n;i++) mp[p[i]] = i; //记录离散化后的数字在p数组的位置
int ans = 0;
for(int i=1;i<=n;i++) if(p[i]!=i) //从第一个数开始查找,如果该数与下标不对应,找到对应的数并与之交换
{
int u = i , v = p[i];
swap(p[i],p[mp[i]]); swap(mp[u],mp[v]); //交换两数和对应下标
ans ++;
}
printf("%d\n",ans);
return 0;
}