CF1041F Ray in the tube

首先 y 肯定是没用的

看到数据范围,

二分?没啥好二分的

dp ? 反正我的状态是存不下的

于是开始手玩

先看看符合条件的点集有什么限制

设点s1, s2, s3, ..., sn 能被同一条射线照到

则它们之间的坐标关系应该以下两条中的一个满足:

  1. s2 - s1 = s3 - s2
  2. s2 - s1 = 2(s3 - s2)

观察上面的式子,与它在同一列的点和它的关系满足条件 1 ,
不同列的点和它的关系满足条件 2

那么其实就是求这个式子:

  maxnx=1 {∑xi=1[(sx - si) / 2 = (2 * k + 1) * Δ] + ∑xj=1[(sx - sj) / 2 = 2 * k * Δ]}

又经过长时间的手玩,突然想到,我应该会有一些方案是一定劣于另一些方案的

比如 Δx = 3, 它就不如 Δx = 1 要优,
因为 Δx = 3 的方案选出来的点是 Δx = 1 的方案选出来的点的真子集

考虑若要一个方案比当前某个方案劣的话, 则它选出来的点是当前方案选的点的真子集,
而要满足这个条件的话,
设当前方案每一次反射 x 坐标偏移量为 Δx ,那么这个劣的方案的 Δx' = (2 * k + 1) * Δx

联想到 bzoj2734集合选数 的构造的方式,列出以下矩阵:

1, 3, 5, 7, 9, 11, 13, 15...

2, 6, 10, 14, 18, 22, 26, 30, ...

4, 12, 20, 28, 36, 44, 52, 60, ...

...

多画几次认为上面的矩阵是没有问题的,相邻两行的方案之间没有包含关系

其实有意义的 Δx 就是 1, 2, 4, 8, ...

所以有意义的 Δx 只有 log(1e9) 个

考虑 dp,空间可行,并不会转移,也不会预处理

于是做到这里我就弃疗了... => 以下来思路自题解

对于固定的 Δx ,同一列的能作为答案的点集在模 Δx 意义下是同余的
那么对于不同列的点,其实它们的坐标减去或加上一个 Δx 就和另一列的点坐标相同了

所以我们就可以在 O(n*logn*log(1e9)) 的时间内做这道题了

log(1e9) 的枚举 Δx = 2^i

之后 nlogn 的将每个数模 Δx ,钦定一列是加(减容易出锅) Δx 之后再模的
将结果 哈希/离散化/map 存一下每次 ++计数器 统计一个 max 就好了

根本没往模那想,显然对于一些等差的东西,模公差是同余的


 代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cstdio>
#include<map>
using namespace std;

const int MAXN = 100005;

int n[2], y[2];
int x[2][MAXN];

inline int rd() {
    register int x = 0;
    register char c = getchar();
    while(!isdigit(c)) c = getchar();
    while(isdigit(c)) {
        x = x * 10 + (c ^ 48);
        c = getchar();
    }
    return x;
}
inline void init() {
    for(int i = 0; i < 2; ++i) {
        n[i] = rd(); y[i] = rd();
        for(int j = 1; j <= n[i]; ++j) x[i][j] = rd();
    }
    return;
}

int main() {
    init();
    if(n[0] == n[1] && n[0] == 1 && x[0][1] == x[1][1]) {
        puts("2");
        return 0;
    }
    int ans = 0;
    for(int i = 1; i <= 1000000000; i <<= 1) {
        int dlt = (i << 1);
        map<int,int> mp;
        for(int j = 0; j < 2; ++j) {
            int pls = (j ? i : 0);
            for(int k = 1; k <= n[j]; ++k) {
                ++mp[(x[j][k] + pls) % dlt];
            }
        }
        for(map<int,int>::iterator it = mp.begin(); it != mp.end(); ++it) ans = max(ans, it->second);
    }
    printf("%d\n", ans);
    return 0;
}

 题解中证明只有 log(1e9) 个有意义的 Δx 方式是反证

令 Δx = m * 2^i 且 m 为奇数

其实这个方案选的点在 Δx' = Δx / m 的方案中也都选了

听起来比我的证明更加理性一些

posted @ 2018-10-12 11:48  EvalonXing  阅读(298)  评论(0编辑  收藏  举报