磁力块题解
1.前言
分块好好 v a n van van 啊。
2.题解
方法:搜索+分块
由于我们只能在 ( x 0 , y 0 ) (x_0, y_0) (x0,y0) 处吸引磁石,所以我们只需要考虑到 ( x 0 , y 0 ) (x_0, y_0) (x0,y0) 的距离就可以了。所以我们的结构体 N o d e Node Node 保存 4 4 4 个变量 d i s dis dis(到 ( x 0 , y 0 ) (x_0, y_0) (x0,y0)的距离的平方), m m m, p p p, r r r (半径的平方)。attention: 为了防止卡精度,我们可以采用平方的办法,而且也就不需要注意数据类型的转换了。
我们要满足两个要求 B . m ≤ A . p , B . d i s ≤ A . r B.m \leq A.p, B.dis \leq A.r B.m≤A.p,B.dis≤A.r。 所以我们可以采用 C D Q CDQ CDQ 的思路,想办法先满足一个变量,不妨将 m m m 从小到大排序,分块后每个块按照 d i s dis dis 从小到大排序。
假设我们现在 b f s bfs bfs 搜索到了 t e m tem tem
如果这一个块的 m m m 的最大值小于 t e m . p tem.p tem.p,那么这个块里的所有元素都满足 m m m 的要求,所以只需要再满足 d i s dis dis 的要求就可以了(这就是我们为什么要按照 d i s dis dis 从小到大排序),由于我们的 d i s dis dis 按照从小到大排序,所以如果这个块的两个元素 i , j i, j i,j(排序后, i i i 在 j j j 的前面), 如果 i i i 不满足要求, 那么 j j j 一定不满足要求。所以我们从这个块的第一个元素开始枚举,不满足要求就 b r e a k break break, 满足要求的话就把 t a ta ta 加进队列,并且从这个块中弹出(每个元素只加入队列一次)。
如果这一个块的 m m m 的最大值大于 t e m . p tem.p tem.p,那么之后的块一定不满足要求了。所以我们只需要暴力遍历这个块后,直接 b r e a k break break 掉就可以了。
由于第二种情况选出的元素不方便从块中删除,所以我们用一个 v i s vis vis 数组标记一下 i i i 号元素是否在队列中或者进入过队列,稍微改一改第一种情况的实现就可以了(具体见代码)。
3.参考代码
#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define int long long
#define LL long long
template <typename T>
void read (T &x) {
x = 0; T f = 1;
char ch = getchar ();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar ();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + ch - '0';
ch = getchar ();
}
x *= f;
}
template <typename T>
void write (T x) {
if (x < 0) {
x = -x;
putchar ('-');
}
if (x < 10) {
putchar (x + '0');
return;
}
write (x / 10);
putchar (x % 10 + '0');
}
template <typename T>
void print (T x, char ch) {
write (x); putchar (ch);
}
template <typename T> T Max (T x, T y) { return x > y ? x : y; }
template <typename T> T Min (T x, T y) { return x < y ? x : y; }
template <typename T> T Abs (T x) { return x > 0 ? x : -x; }
const int Maxn = 25e4;
const int Maxsn = 500;
int n, sx, sy;
struct Node {
int dis, m, p, r;
}a[Maxn + 5];
bool cmp_m (Node x, Node y) {
return x.m < y.m;
}
bool cmp_dis (Node x, Node y) {
return x.dis < y.dis;
}
int Size, Num;
int l[Maxn + 5], r[Maxn + 5], m_max[Maxn + 5];
#define l(x) ((x - 1) * Size + 1)
#define r(x) (Min ((x) * Size, n))
void Build () {
sort (a + 1, a + 1 + n, cmp_m);
Size = sqrt (n); Num = ceil (n * 1.0 / Size);
for (int i = 1; i <= Num; i++) {
l[i] = l (i); r[i] = r (i);
for (int j = l[i]; j <= r[i]; j++)
m_max[i] = Max (m_max[i], a[j].m);
sort (a + l[i], a + 1 + r[i], cmp_dis);
}
}
bool check (Node x, Node y) {
if (y.dis <= x.r && y.m <= x.p) return 1;
else return 0;
}
//check (x, y) 返回 x 号磁石是否能吸引 y 号磁石
bool vis[Maxn + 5];
signed main () {
read (sx); read (sy); read (a[0].p); read (a[0].r); read (n);
a[0].r = pow (a[0].r, 2);
for (int i = 1; i <= n; i++) {
int x, y; read (x); read (y); read (a[i].m); read (a[i].p); read (a[i].r);
a[i].dis = pow (x - sx, 2) + pow (y - sy, 2);
a[i].r = pow (a[i].r, 2);
}
Build ();
int ans = 0;
queue <Node> q; q.push (a[0]);
while (q.size ()) {
Node tem = q.front (); q.pop ();
for (int i = 1; i <= Num; i++) {
if (m_max[i] > tem.p) {//第一不满足的块
for (int j = l[i]; j <= r[i]; j++) {
if (vis[j] == 1) continue;
if (check (tem, a[j])) {//满足要求,加入队列
ans++;
vis[j] = 1;
q.push (a[j]);
}
}
break;
}
while (l[i] <= r[i] && check (tem, a[l[i]])) {//整个块都满足 m 的要求
if (vis[l[i]] == 1) {//进入过队列或在队列中,直接跳过就可以了
l[i]++;
continue;
}
ans++; q.push (a[l[i]++]);//满足要求,加入队列
}
}
}
write (ans);
return 0;
}