实际上完全不需要二分答案,这是一个O(n)的做法
对于物理上相邻的两个开水闸门,我们完全可以考虑能否将其看成是一个闸门,
比如对于 len = 10
l r
1 11
10 1
这样的一对闸门,完全可以合并成一个闸门 10 1,因为当在位置1上的闸门放水的时候,位置10上的闸门放的水已经到达了位置1
那么就可以考虑将若干闸门合并,每个闸门负责一段区间
在这里我用了一个结构体来记录每个闸门的信息
struct interval{
int l; 左边界
int r; 右边界
int mid; 闸门在哪
int ti; 开闸门的时间
};
对于相邻的闸门,分别从两个闸门到达它们的公共边界所用的时间差值一定不超过1
最后只需要遍历所有闸门,求闸门覆盖它们控制的区间所需时间最大值即可
需要注意,相邻两个闸门能否合并成一个的问题
可以分以下两种情况:
1.左边的闸门可以取代右边的闸门
2.右边的的闸门可以(连续)取代左边的闸门
比如 1 100000, 2 10000000, 3 1这样的三个闸门
#include<bits/stdc++.h>
const int MAXN = 1e5 + 10;
using namespace std;
int n, len;
struct node{
int li;
int ri;
};
struct interval{
int l;
int r;
int mid;
int ti;
};
node q[MAXN];
interval inv[MAXN];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin>> n >> len;
for(int i = 1;i <= n;i++) cin>> q[i].li >> q[i].ri;
int cnt = 0;
inv[++cnt].mid = q[1].li;
inv[cnt].l = 1;
inv[cnt].r = len;
inv[cnt].ti = q[1].ri;
for(int i = 2;i <= n;i++)
{
if(q[i].ri >= inv[cnt].ti + (q[i].li - inv[cnt].mid)) continue; // 左边合并右边
else
{
while(cnt && inv[cnt].ti > q[i].ri + q[i].li - inv[cnt].mid) cnt--; // 右边合并左边
inv[++cnt].r = len;
inv[cnt].ti = q[i].ri;
inv[cnt].mid = q[i].li;
int mid = q[i].ri + q[i].li + inv[cnt-1].mid - inv[cnt-1].ti;
if(cnt == 1) inv[cnt].l = 1;
else
{
inv[cnt].l = mid + 1 >> 1;
inv[cnt-1].r = mid >> 1;
}
}
}
int ans = 0;
//遍历闸门
for(int i = 1;i <= cnt;i++)
{
cout<< i << " " << inv[i].l << " " << inv[i].r << " " << inv[i].mid << " " << inv[i].ti << '\n';
ans = max(ans, inv[i].ti + max(inv[i].mid - inv[i].l, inv[i].r - inv[i].mid));
}
cout<<ans<<'\n';
return 0;
}