题目链接:https://www.acwing.com/problem/content/252/
题目给出一些点的坐标,质量,磁力和吸引半径,初始时刻只有一个磁石在(x0,y0)位置,可以通过磁石吸引其他磁石,然后选择磁石继续进行吸引,问最终可以得到多少磁石?
由于磁石的吸引要满足距离小于吸引半径,质量小于等于其磁力,所以可以按照质量进行排序分块,第二个约束在分块內进行排序,保存分块的最大的质量。对于可以拿的磁石都放入队列,进行BFS,完全小于其质量的只要从每段的头部开始取就可以,每段的头部在取完之后要变动到最终不可取的位置。而质量不完全小于等于队首元素的磁力的时候就需要对每个磁石单独进行判断。在对完全满足的进行判断的时候,是否访问过的判断要在while循环中,要保证L[i]在满足两个条件的情况下必须后移。还要注意平方的时候都要变成ll进行判断,否则会wa。
代码:
#include<iostream> #include<cstdio> #include<algorithm> #include<math.h> using namespace std; typedef long long ll; const int maxn = 250010; const int T = 550; struct node{ int x,y,m,p,r; }p[maxn],q[maxn]; int n; int L[maxn],R[maxn],M[maxn]; bool vis[maxn]; inline bool cmp0(node& a,node& b){ return a.m<b.m; } inline ll dis2(node& a){ return (ll)(a.x-q[0].x)*(a.x-q[0].x)+(ll)(a.y-q[0].y)*(a.y-q[0].y); } inline bool cmp1(node& a,node& b){ return dis2(a)<dis2(b); } int main(){ scanf("%d%d%d%d%d",&q[0].x,&q[0].y,&q[0].p,&q[0].r,&n); for(int i=1;i<=n;i++) scanf("%d%d%d%d%d",&p[i].x,&p[i].y,&p[i].m,&p[i].p,&p[i].r); sort(p+1,p+n+1,cmp0);//按照质量进行排序 int t=sqrt(n); int len=t?n/t:n; for(int i=1;i<=t;i++){ L[i]=(i-1)*len+1; R[i]=i*len; M[i]=p[R[i]].m;//分块内部的最大质量 sort(p+L[i],p+R[i]+1,cmp1);//按照距离进行组内的二次排序 } if(R[t]<n){ L[t+1]=R[t]+1; R[++t]=n; M[t]=p[n].m; sort(p+L[t],p+R[t]+1,cmp1); } int ans=0; int l=0,r=1; while(l<r){ int k=0; for(int i=1;i<=t;i++){//找到最后一个块k,前面的都是<=队头磁石的质量的 if(M[i]<=q[l].p)k=i; else break; } for(int i=1;i<=k;i++){ while(L[i]<=R[i] && dis2(p[L[i]])<=(ll)q[l].r*q[l].r){ if(!vis[L[i]]){//在单独处理的块块中被访问 vis[L[i]]=1;//同一个磁石不会算两次 ans++; q[r++]=p[L[i]]; } L[i]++; } } if(t!=k){//不是最后一个块 k++;//单独扫描当前块中的每个元素 for(int i=L[k];i<=R[k];i++){ if(!vis[i] && p[i].m<=q[l].p && dis2(p[i])<=(ll)q[l].r*q[l].r){ vis[i]=1; ans++; q[r++]=p[i]; } } } l++; } cout<<ans<<endl; return 0; }