数列(前缀和,离散化)

 P1667 数列

题目描述

给定一个长度是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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值