数列(前缀和,离散化)

 P1667 数列

题目描述

给定一个长度是n的数列A,我们称一个数列是完美的,当且仅当对于其任意连续子序列的和都是正的。现在你有一个操作可以改变数列,选择一个区间[X,Y]满足Ax +Ax+1 +…+ AY<0,1

输入输出格式

输入格式:

 

第一行一个数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

下面是AC代码

//#include
#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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.summp;             //也可以直接用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;
}

 

你可能感兴趣的:(数列(前缀和,离散化))