250. 磁力块
题意:给你n个散落在二维平面的磁铁,有(m,p,r)三个属性。m表示重量,p表示磁力,r表示作用半径。A磁铁可以将B磁铁吸引过来,当且仅当 p A > = m B A N D r > = d i s ( A , B ) p_{A}>=m_{B} \ \ AND \ \ r>=dis(A,B) pA>=mB AND r>=dis(A,B) ,一开始我们在点 ( x 0 , y 0 ) (x_{0},y_{0}) (x0,y0)处有一块初始磁铁。问最终你可以吸引多少块磁铁到点 ( x 0 , y 0 ) (x_{0},y_{0}) (x0,y0)处。
数据范围:
1≤N≤250000
−10^9 ≤x,y≤ 10^9
1≤m,p,r≤10^9
如果我们可以将另一块m很小,但p,r却很大的磁铁吸引过来,就可以用这块再吸引其他块。所以我们考虑bfs,用bfs来更新当前磁铁可以吸引的其他磁铁,然后将其入队,再更新。这样最多操作n次,即将所有磁铁入队。
接下来我们考虑如何来找可以更新的点,如果暴力的话,最终复杂度是 O ( n 2 ) O(n^2) O(n2)的。
鉴于分块的思想,我们可以按重量排序,然后将其每段分成 n \sqrt n n块,每一块内按与 ( x 0 , y 0 ) (x_{0},y_{0}) (x0,y0)的距离排序,因为最终我们是要找多少个点可以被吸引到 ( x 0 , y 0 ) (x_{0},y_{0}) (x0,y0)处。
比如我们队头元素是k,那么如果一块内的最大值都比k.p要小的话,那么我们二分找距离小于k.r的值然后向前遍历入队,但又因为已经有序且每次都要加入队列,所以我们每次将st[i]++即可。对于最大值比k.p还大的情况,因为这一块已经比k.p大了,那么其后面的块的重量肯定比k.p都大,没有必要找。对这一块我们只要暴力找满足条件的值即可。第一种情况找块最多有 n \sqrt n n次,每次入队均摊是O(1)。第二种自然是在块里找满足条件的值,最大操作 n \sqrt n n次。所以更新点的复杂度为O( n \sqrt n n)。
时间复杂度 O ( n n ) O(n\sqrt n) O(nn)
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<cstring>
#include<string>
#include<algorithm>
#include<fstream>
#include<cmath>
#include<map>
#include<set>
#include<stack>
using namespace std;
typedef long long ll;
const int maxn=3e5+10;
struct node{
int m,p;
ll dis,r;
}a[maxn];
bool cmp(node x1,node y1)
{
return x1.m<y1.m;
}
bool cmp1(node x1,node y1)
{
return x1.dis<y1.dis;
}
int st[maxn],ed[maxn];
int vis[maxn],dp[maxn]; //dp[i]:第i块中重量最大是多少
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
ll x,y;int p,r,n;cin>>x>>y>>p>>r>>n;
for(int i=1;i<=n;i++)
{
ll x1,y1;cin>>x1>>y1>>a[i].m>>a[i].p>>a[i].r;
a[i].dis=(x1-x)*(x1-x)+(y1-y)*(y1-y);
a[i].r*=a[i].r; //dis的平方
}
sort(a+1,a+1+n,cmp); //按重量排序
int sq=sqrt(n);
for(int i=1;i<=sq;i++)
st[i]=sq*(i-1)+1,ed[i]=sq*i,dp[i]=a[ed[i]].m;
ed[sq]=n;dp[sq]=a[n].m;
for(int i=1;i<=sq;i++)
sort(a+st[i],a+ed[i]+1,cmp1); //块里按距离排序
queue<node>q;
int ans=0;
q.push({0,p,0,1ll*r*r});
while(!q.empty())
{
node k=q.front();q.pop();
for(int i=1;i<=sq;i++)
{
if(dp[i]>k.p) { //到这块截至 sqrt(n)
for(int j=st[i];j<=ed[i];j++)
{
if(k.r<a[j].dis) break;
else if(!vis[j]&&k.p>=a[j].m)
{
vis[j]=1;
q.push(a[j]);
ans++;
}
}
break;
}
while(st[i]<=ed[i])
{
if(a[st[i]].dis>k.r) break;
if(!vis[st[i]]) {
ans++;q.push(a[st[i]]);
}
st[i]++;
}
}
}
cout<<ans<<endl;
return 0;
}