给 n n 个长度为 且互不相交的开区间 (xi,xi+l) ( x i , x i + l ) ,每个区间有一个移动速度 v v ,。你可以在某一时刻给所有区间同时增加一个速度 w w ,要求满足 。
求有对多少对 (i,j)(i<j) ( i , j ) ( i < j ) 移动的过程中能同时覆盖原点(坐标为 0 0 的点)。
()
将区间按照初速度分为两组,再按照左端点排序,显然同一组无论如何不可能相交,不同组的有两种情况:
在 0 0 的异侧,且初始移动方向都朝向原点
那么显然一定是合法解。因为你可以等到其中一个到达原点时,立马把它的速度加至 。
这步可以 O(n) O ( n ) 解决。
在 0 0 的同侧
假设有两区间 和 j j , i i 到 的右边的时间是已知的 t=(xj+l−xi)/2 t = ( x j + l − x i ) / 2 ,其他情况同理。
给所有区间同时增加一个速度 w w ,可以视为给坐标轴增加一个速度 。
于是就相当于两区间还是按照原来的速度移动,原点朝向它们移动,那么显然如果原点按照最大速度移动能在 i i 区间跨过 区间前到达 i i 区间的左侧,那么就该区间就一定合法,即当 时合法。
而且如果 i i 和 (xi<xj) ( x i < x j ) 是合法的一对,那么 i i 和 (xj<xk) ( x j < x k ) 也一定是合法的一对,答案有单调性,可以二分。
于是就可以用二分的方法解决,
O(nlogn)
O
(
n
log
n
)
。
当然也可以 Two-Pointer,
O(n)
O
(
n
)
解决。
由于有排序,所以总复杂度还是 O(nlogn) O ( n log n )
#include <bits/stdc++.h>
using namespace std;
vector<int> a, b;
int n, l, w;
long long Ans;
int main()
{
scanf("%d %d %d", &n, &l, &w);
for(int i = 1; i <= n; ++i)
{
int x, v;
scanf("%d %d", &x, &v);
if(v == 1) a.push_back(x);
else b.push_back(x);
}
sort(a.begin(), a.end());
sort(b.begin(), b.end());
int d1 = 0, d2 = 0, d3 = 0;
for(auto i : a) if(i < 0) ++d1;
for(auto i : b) if(i + l > 0) ++d2;
Ans += 1ll * d1 * d2;
if(w > 1)
{
int lim = 0;
for(; lim < b.size() && b[lim] + l <= 0; ++lim);
for(auto i : a) if(i + l < 0)
{
int l = -1, r = lim;
while(r - l > 1)
{
int mid = l + r >> 1;
if(b[mid] < i){ l = mid; continue; }
double t = 1.0 * (b[mid] + ::l - i) / 2;
if(-w * t < i + t) r = mid;
else l = mid;
}
Ans += lim - r;
}
for(auto i : a) if(i >= 0)
{
int l = lim - 1, r = b.size();
while(r - l > 1)
{
int mid = l + r >> 1;
if(b[mid] < i){ l = mid; continue; }
double t = 1.0 * (b[mid] + ::l - i) / 2;
if(w * t > i + t) r = mid;
else l = mid;
}
Ans += b.size() - r;
}
}
printf("%lld\n", Ans);
return 0;
}