cf 1358E. Are You Fired?(双指针+贪心)

题目:传送门

思路:若存在答案 k , 那么一定有答案 m * k (其中m=1、2、3、...... 且 m * k <= n ),可推出 一定存在答案 k' > n/2 (后者是前者的必要条件);  【显然 两个相邻长度为 k 的区间的和都>=0 , 合并后也>=0;】

   这道题的精髓也就在 不等式 k' > n/2 上。如果你对接下来的叙述有任何疑惑,请记住 k' > n/2 。

   当 x >= 0 , 对于任意左端点 L <= (n+1)/2 , 对应的长度为 k' 的区间 [ L , L + k' - 1 ] , 而 L + k' - 1 > (n+1)/2 (由 k' > n/2 得),故 L 对应的区间与最后的 [n/2] 相交,而 x >= 0 ,  因此将右端点右移可使区间和变大,对于每个 L 对应的区间,右端点取到n可使区间和最大,那么只需要判断 [1,n] 的区间和即可 。 (最后[n/2] 个数字都是>=0 的,因此如果存在答案k  --> k' > n/2, 那么 k'' = n 也应该同样满足; 后者是前者的必要条件)

   当 x < 0 , 对于任意左端点 L <= (n+1)/2 , 对应的长度为 k' 的区间 [ L , L + k' - 1 ]  , 若 区间和 sum <=0 , 那么就将其的右端点左移 (区间右边部分都是 x < 0),使其满足 sum > 0 。(双指针思想+贪心)那么可以设置 初始L = 1 和 初始答案 k = n , k越大,那么长度为k的区间数就越少(区间和的约束也就越少) ,且对于 区间[ L , L + k -1 ] ,其区间和若 > 0 ,那么 [ L , R ] 的区间和也 > 0 , (n+1)/2 < R < L + k -1 ,若区间和 < 0 , 则 k-- 。 对于 L = i 的对应的区间 ,当k--时 , 则 L' < i 的区间长度也变短 ,其区间和减去一个负数,且之前已经处理过的L ' 对应的区间和是 > 0 的,那么减去负数也还是 > 0 。

   一开始,发现 k 类似于一个多峰值函数,但采用一定贪心策略能够使其具有单调性,之后就可以用双指针求解了。至于快速获取区间和,可以先预处理前缀和。

#include<bits/stdc++.h>
/*
#include<cstdio>
#include<cmath>
#include<cstring>
#include<vector>
#include<cctype>
#include<queue>
#include<algorithm>
#include<map>
#include<set>
*/
#pragma GCC optimize(2)
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
typedef pair<double,double> pdd;
const int N=1e6+5;
const int M=1e4+5;
const int inf=0x3f3f3f3f;
const LL mod=1e9+7;
const double eps=1e-9;
const long double pi=acos(-1.0L);
#define ls (i<<1)
#define rs (i<<1|1)
#define fi first
#define se second
#define pb push_back
#define mk make_pair
#define mem(a,b) memset(a,b,sizeof(a))
LL read()
{
    LL x=0,t=1;
    char ch;
    while(!isdigit(ch=getchar())) if(ch=='-') t=-1;
    while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); }
    return x*t;
}
int a[N];
LL sum[N];
int main()
{
    int n=read();
    for(int i=1;i<=(n+1)/2;i++) a[i]=read();
    int x=read();
    for(int i=(n+1)/2+1;i<=n;i++) a[i]=x;
    for(int i=1;i<=n;i++) sum[i]=a[i]+sum[i-1];
    if(x>=0)
    {
        if(sum[n]>0) printf("%d
",n);
        else printf("-1
");
        return 0;
    }
    int len=n;
    for(int i=1;i<=n;i++)
    {
        int j=i+len-1;
        if(j>n||len<=n/2) break;
        LL t=sum[j]-sum[i-1];
        while(t<=0&&len>n/2) t-=x,len--;
    }
    if(len<=n/2) printf("-1
");
    else printf("%d
",len);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/DeepJay/p/12976595.html