「Codeforces 989D」A Shade of Moonlight

n n 个长度为 l 且互不相交的开区间 (xi,xi+l) ( x i , x i + l ) ,每个区间有一个移动速度 v v v{1,1}。你可以在某一时刻给所有区间同时增加一个速度 w w ,要求满足 |w|wmax
求有对多少对 (i,j)(i<j) ( i , j ) ( i < j ) 移动的过程中能同时覆盖原点(坐标为 0 0 的点)。
(1n105,1l,wmax108)

将区间按照初速度分为两组,再按照左端点排序,显然同一组无论如何不可能相交,不同组的有两种情况:

0 0 的异侧,且初始移动方向都朝向原点

那么显然一定是合法解。因为你可以等到其中一个到达原点时,立马把它的速度加至 0(wmax1)

这步可以 O(n) O ( n ) 解决。

0 0 的同侧

假设有两区间 i j j (xi<xj<0) i i j 的右边的时间是已知的 t=(xj+lxi)/2 t = ( x j + l − x i ) / 2 ,其他情况同理。

给所有区间同时增加一个速度 w w ,可以视为给坐标轴增加一个速度 w

于是就相当于两区间还是按照原来的速度移动,原点朝向它们移动,那么显然如果原点按照最大速度移动能在 i i 区间跨过 j 区间前到达 i i 区间的左侧,那么就该区间就一定合法,即当 wmax×t<xi+t 时合法。

而且如果 i i j (xi<xj) ( x i < x j ) 是合法的一对,那么 i i k (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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值