题意
传送门 AcWing 250 磁力块
题解
将满足 r i ∗ r i ≥ ( x j − x 0 ) 2 + ( y j − y 0 ) 2 , p i ≥ m j r_i*r_i\geq (x_j-x_0)^2+(y_j-y_0)^2,p_i\geq m_j ri∗ri≥(xj−x0)2+(yj−y0)2,pi≥mj 的磁石 i i i 向磁石 j j j 连一条有向边,以磁石 L L L 为起点进行 B F S BFS BFS,所遍历到的磁石数量(除了 L L L)即答案。
朴素的求解每一块磁石能吸引的所有磁石,时间复杂度 O ( N 2 ) O(N^2) O(N2)。考虑分块,将磁石按照距离排序,分为 N \sqrt N N 块,并对每一块按照磁石质量排序;每次用队首元素吸引磁石时,必然存在一个位置 k k k,使分块 1 1 1 到 k − 1 k-1 k−1 中的磁石距离都满足约束条件,分块 k k k 中可能有部分磁石距离满足约束条件,编号大于 k k k 的分块中磁石都不满足距离约束条件。
对 1 1 1 到 k − 1 k-1 k−1 的分块从左界开始扫描,直到不满足约束条件,并移动块的左界到当前位置,每一块至多被扫描 N + N N+\sqrt N N+N 次,对每一块而言均摊为 O ( 1 ) O(1) O(1);分块 k k k 直接枚举块内元素,复杂度 O ( N ) O(\sqrt N) O(N)。总时间复杂度 O ( N N ) O(N\sqrt N) O(NN)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 250005, maxq = 505;
struct node
{
int m, p;
ll d, r;
} A[maxn];
int N, tot, res, L[maxq], R[maxq];
ll _x, _y, D[maxn];
bool used[maxn];
queue<int> Q;
bool cmp_d(const node &a, const node &b) { return a.d < b.d; }
bool cmp_m(const node &a, const node &b) { return a.m < b.m; }
int main()
{
scanf("%lld%lld%d%lld%d", &_x, &_y, &A[0].p, &A[0].r, &N);
A[0].r *= A[0].r;
for (int i = 1; i <= N; ++i)
{
ll x, y;
scanf("%lld%lld%d%d%lld", &x, &y, &A[i].m, &A[i].p, &A[i].r);
x -= _x, y -= _y;
A[i].r *= A[i].r, A[i].d = x * x + y * y;
}
sort(A + 1, A + N + 1, cmp_d);
int t = sqrt(N);
t = t ? t : N;
for (int i = 1; i <= N; i += t)
{
L[++tot] = i, R[tot] = min(i + t, N + 1), D[tot] = A[R[tot] - 1].d;
sort(A + L[tot], A + R[tot], cmp_m);
}
Q.push(0);
while (Q.size())
{
++res;
int k = Q.front();
Q.pop();
int p = A[k].p;
ll r = A[k].r;
for (int i = 1; i <= tot; ++i)
{
if (D[i] > r)
{
for (int j = L[i]; j < R[i]; ++j)
if (!used[j] && A[j].d <= r && A[j].m <= p)
used[j] = 1, Q.push(j);
break;
}
while (L[i] < R[i] && A[L[i]].m <= p)
{
if (!used[L[i]])
Q.push(L[i]);
++L[i];
}
}
}
printf("%d\n", --res);
return 0;
}