题目描述:
自习室内有一个智能灯。
在 0 时刻,管理员会将打开电闸,并将灯点亮。
在 M 时刻,管理员会直接拉下电闸,此时,如果灯处于点亮状态,则会因为断电而熄灭。
在 0∼M 之间有 n 个不同时刻,不妨用 a1,a2,…,an 表示,其中 0<a1<a2<…<an<M。
在这 n 个时刻中的每个时刻,管理员都会拨动一次智能灯的开关,使灯的状态切换(亮变灭、灭变亮)。
现在,你可以最多额外指定一个时刻(也可以不指定),让管理员在此时刻也拨动开关一次。注意选定的时刻不能与 a1,a2,…,an 相等。
你的目的是让亮灯的总时长尽可能长。
输出这个最大亮灯总时长。
输入格式
第一行包含整数 T,表示共有 T 组测试数据。
每组数据,第一行包含两个整数 n 和 M。
第二行包含 n 个整数 a1,a2,…,an。
输出格式
输出一个整数,表示最大亮灯总时长。
数据范围
1≤T≤30, 1≤n≤10^5, 2≤M≤10^9, 0<a1<a2<…<an<M。 同一测试点内所有 n 的和不超过 10^5。
输入样例:
3 3 10 4 6 7 2 12 1 10 2 7 3 4
输出样例:
8 9 6
思路:
设a0 = 0,an+1 = m,在[a0,an+1]区间内,0时刻灯被点亮,所以亮灯时间为[a0,a1],[a2,a3],[a4,a5]……等奇数区间,而对应的不亮灯的区间为[a1,a2],[a3,a4],[a5,a6]……等偶数区间。
我们假设在[ai,ai+1]区间内加入一次操作,则[a0,ai]区间内的亮灯时间不受影响,而[ai+1,an+1]区间内的亮灯时间为加入操作前的不亮灯的时间,状态翻转。所以我们需要记录原来的亮灯时间light[]和不亮灯时间dark[],这里可以用到前缀和。[a0,ai]区间的亮灯时间为light[i]-light[0],[ai+1,an+1]区间的亮灯时间对应原来的不亮灯时间dark[n+1]-dark[i+1]。
for(int i=1;i<=n;i++){
light[i] += light[i-1];
dark[i] += dark[i-1];
if(i % 2 == 1) light[i] += a[i] - a[i-1]; //亮灯时间为原来的奇数区间
else dark[i] += a[i] - a[i-1];
}
也可以使用后缀和来求亮灯时间和不亮灯时间。(注意边界n的值)
for(int i=n-1;i>=0;i--){ //后缀和
light[i] = light[i+1];
dark[i] = dark[i+1];
if(i % 2 == 0) light[i] += a[i+1] - a[i]; //下标值要注意判断
else dark[i] += a[i+1] - a[i];
}
而在[ai,ai+1]加操作的区间内,亮灯时间为t-1,也就是a[i+1]-a[i]-1,若[ai,ai+1]区间大小为1,无法加入更多的操作,需要跳出判断。
另外还要注意,若不加操作,亮灯时长就是原先的总亮灯时间light[n]。
代码:
利用前缀和计算
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
const int N = 1e5+5;
int a[N];
int light[N],dark[N];
int main()
{
ios::sync_with_stdio(false);
int t;
cin>>t;
while(t--){
int n,m;
cin>>n>>m;
memset(light,0,sizeof(light));
memset(dark,0,sizeof(dark));
light[n] = dark[n] = 0;
for(int i=1;i<=n;i++) cin>>a[i];
a[0] = 0,a[++n] = m; //n值+1
for(int i=1;i<=n;i++){
light[i] += light[i-1];
dark[i] += dark[i-1];
if(i % 2 == 1) light[i] += a[i] - a[i-1];
else dark[i] += a[i] - a[i-1];
}
int res = light[n];
for(int i=0;i<n;i++){
int cnt = a[i+1]-a[i];
if(cnt == 1) continue;
res = max(res,light[i] - light[0] + dark[n] - dark[i+1] + cnt-1);
}
cout<<res<<'\n';
}
return 0;
}
利用后缀和计算
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const int N = 1e5+5;
int a[N];
int light[N],dark[N];
int main()
{
ios::sync_with_stdio(false);
int t;
cin>>t;
while(t--){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
a[++n] = m; //n值+1
light[n] = dark[n] = 0;
for(int i=n-1;i>=0;i--){ //后缀和
light[i] = light[i+1];
dark[i] = dark[i+1];
if(i % 2 == 0) light[i] += a[i+1] - a[i];
else dark[i] += a[i+1] - a[i];
}
int res = light[0];
for(int i=0;i<n;i++){
int cnt = a[i+1]-a[i];
if(cnt == 1) continue;
res = max(res,light[0] - light[i] + dark[i+1] + cnt-1);
}
cout<<res<<'\n';
}
return 0;
}