Array and Segments (Hard version) CodeForces - 1108E2 (差分+思维)

利用差分数组解决数组区间操作问题,寻找最大值与最小值之差的最大值。通过优化,将时间复杂度降低到O(NM+M²),减少了重复计算。
摘要由CSDN通过智能技术生成

题目链接

题意:
给定长度为n的数列,给定m个区间,可以选择某些区间,每个区间被选择等价为让这个区间的元素的值都减1. 问选完后数列最大值与最小值之差,最大可以为多少。

(2<=n<=2e5,1<=m<=300)

思路:设最佳方案选完,最小值位置在a,最大值位置在b。
任意一个区间有4种情况,包含a不包含b,包含b不包含a,包含ab,不包含ab

第一种的区间是必选的 第二种不能选 后两种无所谓

于是在选择前,我们假设index元素是最大值,然后选择所有不包含index的区间, 利用差分数组 ,在O(1)完成区间更新,O(n)求出极差

但是这样做法是O(n²),我们考虑得优化方法,

我们知道,对于某个区间上的元素,如果他们被选择作为预先假设的最大值,对其余区间的选择是没有影响,也就是其余区间中不包含这些元素的区间集合是等价的,那么这些元素只需要假设一次就行。

比如:这张图描述了4个区间
这个图里,4个区间,我们可以看出
[l1,l2-1] ,[l2,r1] ,[r1+1,r2], [l3,l4-1] ,[l4,r4] [r4+1,r3] 这些区间上,任意一个区间上的点作为假设的最大值都是等效的。

我们只需要分别假设 :l1 ,l2 ,r1+1 ,l3 ,l4 ,r4+1 就可以了,数量级和m是一样的 。

所以这样时间复杂度为O(NM+M²)

#include<bits/stdc++.h>
using namespace std;
#define Please return
#define AC 0;
#define LL  long long 
#define ULL  unsigned long long 
const LL INF=2e18+7;
const int maxn=2e5+7;

int n,m;
LL a[maxn];
LL de[maxn];//差分数组
int id;
LL ans;
set<int> key;//存区间端点
int l[maxn],r[maxn];
int main()
{
    scanf("%d %d", &n, &m);
    for(int i=1; i<=n; i++)
    {
        scanf("%I64d", a+i);
    }
    for(int i=1; i<=m; i++)
    {
        scanf("%d %d",&l[i],&r[i]);
        key.insert(l[i]),key.insert(r[i]+1);
    }
    if(n==1)
    {
        printf("0\n0\n");
        return 0;
    }
    ans=*max_element(a+1,a+n+1)-*min_element(a+1,a+n+1);//一个区间也不选
    id=0;//一个区间也不选,id就是0 
    for(auto i:key)//枚举区间端点
    {
        memset(de,0,sizeof(de));
        LL mmax=-INF,mmin=INF;
        for(int j=1; j<=m; j++)
        {
            if(l[j]<=i && i<=r[j]) continue;
            de[l[j]]--,de[r[j]+1]++;
        }
        for(int j=1;j<=n;j++)
        {
            de[j]+=de[j-1]+a[j]-a[j-1];
            mmax=max(mmax,de[j]);
            mmin=min(mmin,de[j]);
        }
        if(ans<mmax-mmin)
        {
            ans=mmax-mmin;
            id=i;
        }
    }
    printf("%I64d\n",ans);
    vector<int> t;
    for(int i=1;i<=m && id;i++)
    {
        if(l[i]<=id && id<=r[i]) continue;
        t.push_back(i);
    }
    printf("%d\n",(int)t.size());
    for(auto i:t) printf("%d ",i);
    Please AC
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值