2016百度之星复赛 1003 拍照 扫描线

原题见1003

有n条船平行于x轴,已知初始时刻的位置,向平行于x轴正向或反向走。所有船速度大小一致。现在海哥站在x轴上,要给船拍照,角度为朝向+y方向的固定90度,可以在任意时刻、任意位置拍,问何时拍下的完整的船数量最多。
这里写图片描述

思路

对于横坐标为[x, y] 纵坐标为z的船而言,海哥可以移动的区间为[y-z, x+z]且y-z x+z。对于同向运动的船,它们在任意时刻的相对位置不发生改变,则可以记录下每个位置可见的船数量 bi 。得到两个方向的 bi 以后,相当于要求以某个位置为分界线,作为两个方向 bi 的最大值的相交位置。故要求向左走的船的后缀最大值 m0i ,以及向右走的船的前缀最大值 m1i ,O(N)扫一遍得到 max[m0i+m1i]

扫描线

输入多个线段位置,输出各个位置重叠的线段个数。
输入一个线段[l,r]时,将位置s[l]++, s[r+1]–, 则统计如下:

b[0] = s[0];
for(int i = 1;i < maxN;i++)
    b[i] = b[i-1] + s[i];

离散化

由于横坐标的范围在 106 ~ 106 ,点数量在 2×105 ,因此可以将横坐标离散化。

附代码

/*--------------------------------------------
 * File Name: 2016百度之星复赛 1003
 * Author: Danliwoo
 * Mail: Danliwoo@outlook.com
 * Created Time: 2016-05-29 17:18:58
--------------------------------------------*/
#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
using namespace std;
#define N 100100
struct node
{
    int x, y;
    node(){}
    node(int x, int y): x(x), y(y) {}
    void pr(){
        printf("pr -> %d %d\n", x, y);
    }
}p[2][N];
int a[2*N], b[2][N], tn[2], an, num[2*N], s[2][N], m[2][N];
int find(int x){
    int l = 0, r = an-1;
    while(a[l] != x){
        int mid = (l+r)/2;
        if(a[mid] == x) return mid;
        if(a[mid] > x) r = mid-1;
        else l = mid+1;
    }
    return l;
}
void solve(){
    memset(num, 0, sizeof(num));
    //left 后缀最大值
    m[0][an-1] = b[0][an-1];
    for(int i = an-2;i >= 0;i--)
        m[0][i] = max(m[0][i], b[0][i]);

    //right 前缀最大值
    m[1][0] = b[1][0];
    for(int i = 1;i < an;i++)
        m[1][i] = max(m[1][i-1], b[1][i]);

    int ans = 0;
    for(int i = 0;i < an;i++)
        ans = max(ans, m[0][i]+m[1][i]);
    printf("%d\n", ans);
}
int main()
{
    int T, o = 0;
    scanf("%d", &T);
    while(T--){
        int n, x, y, z, d;
        scanf("%d", &n);
        memset(s, 0, sizeof(s));
        memset(b, 0, sizeof(b));
        memset(m, 0, sizeof(m));
        memset(a, 0, sizeof(a));
        tn[0] = tn[1] = an =  0;
        for(int i = 0;i < n;i++){
            scanf("%d%d%d%d", &x, &y, &z, &d);
            if(y-z > x+z) continue;
            d = (d+1)/2;
            p[d][tn[d]++] = node(y-z, x+z);
            a[an++] = y-z;
            a[an++] = x+z;
        }
        sort(a, a+an);    //离散化
        unique(a, a+an);
        a[an] = a[an-1];
        for(int i = 0;i < an;i++)
            if(a[i] >= a[i+1]){
                an = i+1;
                break;
            }
        for(int k = 0;k < 2;k++){
            for(int i = 0;i < tn[k];i++){
                int l = find(p[k][i].x);
                int r = find(p[k][i].y);
                s[k][l]++;
                s[k][r+1]--;
            }
            b[k][0] = s[k][0];
            for(int i = 1;i <= an;i++)
                b[k][i] += b[k][i-1] + s[k][i];
        }
        printf("Case #%d:\n", ++o);
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值