题意:
给定长度为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个区间,我们可以看出
[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
}