https://www.acwing.com/problem/content/252/
https://ac.nowcoder.com/acm/contest/1034/B
题目
在一片广袤无垠的原野上,散落着N块磁石。
每个磁石的性质可以用一个五元组(x,y,m,p,r)描述,其中x,y表示其坐标,m是磁石的质量,p是磁力,r是吸引半径。
若磁石A与磁石B的距离不大于磁石A的吸引半径,并且磁石B的质量不大于磁石A的磁力,那么A可以吸引B。
小取酒带着一块自己的磁石L来到了这片原野的(x0,y0)处,我们可以视磁石L的坐标为(x0,y0)。
小取酒手持磁石L并保持原地不动,所有可以被L吸引的磁石将会被吸引过来。
在每个时刻,他可以选择更换任意一块自己已经获得的磁石(当然也可以是自己最初携带的L磁石)在(x0,y0) 处吸引更多的磁石。
小取酒想知道,他最多能获得多少块磁石呢?
$1\leqslant N\leqslant 250000$
$−10^9\leqslant x,y\leqslant10^9$
$1\leqslant m,p,r\leqslant10^9$
题解
这是个需要二维查询的题目,首先可以BFS。因为是分块例题= =
分块方法为:先将整个区间按照质量排序,分为D块,再把D块内按照距离排序
第一维用O(D)的时间找到所有质量都小于等于吸力的块,第二维每一块都顺序遍历确定距离比半径小的,并且保证每一个只遍历一次(因为距离已经排好了)
剩下的块直接遍历。
每个元素只遍历一次,但是在bfs的每一步,每个块都要遍历一次,还要考虑剩下的块,所以时间复杂度是$\mathcal{O}(n\times (1+n/D+D))$,为了不升阶,可以取$D=\sqrt{n}$
AC代码
#include<bits/stdc++.h> using namespace std; #define REP(i,a,b) for(register int i=(a); i<(b); i++) #define REPE(i,a,b) for(register int i=(a); i<=(b); i++) #define PERE(i,a,b) for(register int i=(a); i>=(b); i--) #ifdef sahdsg #define DBG(...) printf(__VA_ARGS__) #else #define DBG(...) void(0) #endif typedef long long ll; #define MAXN 250007 struct node{ int m,p; ll dis, r; }; bool cmp1(const node&l, const node&r) { return l.m<r.m; } bool cmp2(const node&l, const node&r) { return l.dis<r.dis; } int D,L; node arr[MAXN]; int M[MAXN]; int pos[MAXN],ed[MAXN]; #define x0 fadfjklwa #define y0 jfdalskas int x0,y0,pl,n; ll rl; int ans; queue<int> q; bool vis[MAXN]; void go() { ans=0; q.push(-1); REP(i,0,n) { vis[i]=0; } while(!q.empty()) { int now=q.front(); q.pop(); if(~now) { pl = arr[now].p; rl = arr[now].r; } int i=0; for(;i<D &&M[i]<=pl;i++) { for(;pos[i]<ed[i] && arr[pos[i]].dis<=rl; pos[i]++) if(!vis[pos[i]]){ vis[pos[i]]=1; ans++; q.push(pos[i]); } } if(i<D) { REP(j,pos[i],ed[i]) if(!vis[j]) { if(arr[j].dis<=rl && arr[j].m<=pl) { vis[j]=1; ans++; q.push(j); } } } } } int main() { scanf("%d%d%d%lld%d", &x0, &y0, &pl, &rl, &n);rl*=rl; REP(i,0,n) { ll x,y; int m,p,r; scanf("%lld%lld%d%d%d", &x, &y, &m, &p, &r); x-=x0, y-=y0; arr[i]=(node){m,p,x*x+y*y,(ll)r*r}; } sort(arr,arr+n,cmp1); L=sqrt((double)n); if(L==0) L=1; D=n/L; pos[0]=0; REP(i,0,D) { ed[i]=pos[i+1]=pos[i]+L; M[i]=arr[ed[i]-1].m; sort(arr+pos[i], arr+ed[i],cmp2); } if(pos[D]<n) { ed[D]=n; M[D]= arr[n-1].m; sort(arr+pos[D], arr+ed[D],cmp2); pos[++D]=n; } go(); printf("%d\n", ans); }