CF1041F Ray in the tube

转自 https://www.luogu.com.cn/blog/MrHY43245/solution-cf1041f

解题思路

本题求解的是打到的传感器数量。首先可以发现,上下边缘的纵坐标,即 y 1 y_1 y1 y 2 y_2 y2 对答案是没有影响的。我们只需要关注传感器的横坐标即可。同样的,激光的发出点在上边缘还是下边缘对答案也没有影响,因此我们不妨设激光发出点总是在上边缘。

而能够被激光打到的传感器横坐标的形式与等差数列有相似之处,即最终结果只与发出点的横坐标(首项 x x x),和每次从一个边缘到另一个边缘,经过横坐标的差值(公差 d d d)有关。对于给定的 xx 和 dd,这束激光能覆盖到的点必定是同边缘上的 x + 2 k × d , k ∈ N x+2k\times d,k\in\mathbb{N} x+2k×d,kN,和另一边缘上的 x + ( 2 k + 1 ) × d , k ∈ N x+(2k+1)\times d,k\in\mathbb{N} x+(2k+1)×d,kN

从数据范围来看,直接枚举 xx 和 dd 的时间复杂度太高,显然不是正解。但我们可以从这个思路出发,看看有什么可优化的地方或者一些特别的性质。

既然本题需要我们打到尽可能多的传感器,那直觉上来说,公差越小,打到传感器的概率就越大。那直接把 d d d 设为 1 1 1 可行吗?从题目样例 1 1 1 来看就不行。样例 1 1 1 中, d = 2 d=2 d=2 时经过的传感器比 d = 1 d=1 d=1 时多。

但是,当我们再构造几组数据时,发现有时这个结论是成立的。比如, d = 5 d=5 d=5 时,构造以下例子:

5 0
1 11 21 31 41
5 1
6 16 26 36 46

在这组数据中,虽然原本是按照 d = 5 d=5 d=5 构造的, d = 1 d=1 d=1 时也能打到所有传感器,而且还能覆盖到更多的点。

再举一些例子,会发现公差为奇数时,其结果一定不会比 d = 1 d=1 d=1 时更优。

于是,我们也可以推出: d = 2 × ( 2 k + 1 ) , k ∈ Z + d=2\times(2k+1),k\in \mathbb{Z^+} d=2×(2k+1),kZ+ 时,其结果一定不会比 d = 2 d=2 d=2 时更优。

更广泛地说,我们可以用 d = 2 l , l ∈ N d=2^l,l\in\mathbb{N} d=2l,lN 来覆盖所有 d = 2 l × ( 2 k + 1 ) , l ∈ N , k ∈ Z + d=2^l\times(2k+1),l\in\mathbb{N},k\in\mathbb{Z^+} d=2l×(2k+1),lN,kZ+ 覆盖到的点。

这个结论不难证明。对于任意一个 d = 2 l × ( 2 k + 1 ) d=2^l\times(2k+1) d=2l×(2k+1) 所能达到的同边缘点 x + 2 × d x + 2 × d x+2\times dx+2×d x+2×dx+2×d,都有 x + 2 × d = x + 4 × ( 2 k + 1 ) x+2\times d = x+4\times(2k+1) x+2×d=x+4×(2k+1)

因此 2 l 2^l 2l 也能覆盖到。不同边缘的点同理。

于是, d d d 的范围就从 1 0 9 10^9 109 缩小到了 log ⁡ ( 1 0 9 ) \log(10^9) log(109)。由此我们就可以进行枚举了。

枚举的方法是对每一个 d d d,计算每一个传感器所对应横坐标最小的首项 x x x,然后找到能够打到最多传感器的 xx 即可。由于 xx 的范围为 [ 0 , 1 0 9 ] [0,10^9] [0,109],我们需要使用一个 m a p map map 来记录每一个 x x x 所能打到的传感器数量。由于 n n n m m m 同阶,本题的时间复杂度为 O ( n log ⁡ n ⋅ log ⁡ ( 1 0 9 ) ) O(n\log n\cdot\log(10^9)) O(nlognlog(109))

本题还有一种特殊情况,即为上下边缘都只有一个传感器,且 a 1 = b 1 a_1=b_1 a1=b1 。此时 d = 0 d=0 d=0,无法计算到。这种情况只需要把答案的初始值设为 2 2 2,这样在取 max ⁡ \max max 操作时就可以绕过这个问题。

代码如下:
#include <bits/stdc++.h>
using namespace std;

const int maxN = 100005;
int n, m, y, a[maxN], b[maxN];
int ans = 2;        //答案初始化
map <int, int> M;

int main() {
    cin >> n >> y;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    cin >> m >> y;
    for (int i = 1; i <= m; i++) {
        cin >> b[i];
    }
    for (int k = 0; k < 30; k++) {
        int d = (1 << k);
        for (int i = 1; i <= n; i++) {
            M[a[i] % (2 * d)]++;    //计算能打到a[i]的,横坐标最小的激光发出点
            ans = max(ans, M[a[i] % (2 * d)]);
        }
        for (int i = 1; i <= m; i++) {
            M[(b[i] + d) % (2 * d)]++;//计算能打到b[i]的,横坐标最小的激光发出点
            ans = max(ans, M[(b[i] + d) % (2 * d)]);
        }
        M.erase(M.begin(), M.end());    //每次刷新map,防止枚举不同d时重复计算
    }
    cout << ans << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值