https://codeforces.com/gym/101911/problem/L
题意
在二维坐标系中给出两个直线 y = a, y = b。在这两条直线上分别有n个和m个传感器,可以任意选两个传感器A,B,发射一束A->B的激光,这个激光遇到直线就会反射,问最多会经过多少个传感器。
1<=n,m<=3e5
题解
先解决一个问题,已知一条射线,如何快速求出经过的传感器个数?
对于上下直线分开讨论。
- 假设如图所示,从下往上的射线在下直线的跨度是 c = ( x b − x a ) ∗ 2 c = (x_b-x_a)*2 c=(xb−xa)∗2,对于每个传感器令其坐标为 x u x_u xu,只需 满足 x u ≡ x a m o d c x_u \equiv x_a \ mod\ c xu≡xa mod c即在这个射线上。
- 对于上直线,同理,只需满足 x u + c / 2 ≡ x a m o d c x_u+c/2 \equiv x_a \ mod\ c xu+c/2≡xa mod c
这两条直线间隔的距离是无所谓的,这个只决定射线的斜率而已。
一个很直观的做法就是枚举上下所有的两个点,然后取最大值,但这个复杂度不允许
O
(
n
2
m
2
)
O(n^2m^2)
O(n2m2)。
画画图可以发现,一些c比较大的射线能用比它小的射线代替,例如c = 6可以被 c = 2代替,但是c = 4 不能被 c = 2 代替。
多尝试一下发现只要 c = 2 l c = 2^l c=2l的射线都不可被替代, c = 2 l ∗ 奇 数 c = 2^l*奇数 c=2l∗奇数都可以被 c = 2 l c = 2^l c=2l的所代替。
所以我们只需在每个传感器处枚举 l o g ( 1 e 9 ) log(1e9) log(1e9)根射线即可。时间复杂度为 O ( ( n + m ) l o g ( 1 e 9 ) l o g ( n ) ) O((n+m)log(1e9)log(n)) O((n+m)log(1e9)log(n))
代码
#include <bits/stdc++.h>
using namespace std;
#define FOR0(a,b) for(int i = a; i < b; ++i)
#define FORE(a,b) for(int i = a; i <= b; ++i)
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 1e5+5;
map<ll,ll> mp1,mp2;
ll n,m;
ll a[maxn], b[maxn];;
int main() {
int y;
scanf("%lld%lld", &n,&y);
for(int i = 0; i < n; ++i)
scanf("%lld", &a[i]);
scanf("%lld%lld", &m, &y);
for(int i = 0; i < m; ++i)
scanf("%lld", &b[i]);
ll c = 2;
ll ans = 2;
for(; c <= 1e9; c <<= 1) {
mp1.clear();mp2.clear();
for(int i = 0; i < n; ++i)
mp1[a[i]%c]++;
for(int i = 0; i < m; ++i)
mp2[b[i]%c]++;
for(int i = 0; i < n; ++i)
ans = max(ans, mp1[a[i]%c]+mp2[(a[i]+c/2)%c]);
for(int i = 0; i < m; ++i)
ans = max(ans, mp2[b[i]%c]+mp1[(b[i]+c/2)%c]);
}
printf("%lld\n", ans);
return 0;
}