这题走投无路时只能用分块优化暴力。
数据量级是10^5,感觉跑不过,结果A了… …
有两个限制条件。那先按一个排序并将其分块,块内用第二个条件排序。这样可以在较快的时间内找到同时符合两个条件的元素。
具体大致操作步骤如下:
- 先按距离排序,再分块,分块时记录下块的”距离代表”,再将每一块内部按质量排序
- 取出队首得到当前距离和磁力。重复2、3、4直到队列为空
- 二分找到距离代表<=半径的最右边的块,记为r
- 块1~块(r-1)一块一块处理,块r暴力处理。将合法的磁石加入队列
下面讲解步骤中的几个细节和关键:
Q1:距离代表是啥?
A1:距离代表就是块内按质量排序前,块左端的磁石的距离。
Q2:为什么块r要暴力处理?而不是跟前面的一起处理?
A2:你想啊,按质量排序后每块的左端点的磁石的距离就不一定是最小的了。第r块的距离代表是第r块中最小的距离,那么第r块中的磁石的距离就不一定全部<=半径。因为(r-1)块的最大距离也是<=第r的距离代表的,所以1到(r-1)块内的磁石的距离均<=半径,所以可以一块一块处理。
Q3:如何一块一块处理?
A3:其实一开始我是用并查集维护的,这样好写。但是T了,因为数据比10^5大,比较极限,这时再套一个logn的复杂度受不了。于是后来对于我对每一块开一个位置标记。每当块内当前磁石push进队列后,当前块的标记++。每次循环从标记处开始。这样就保证了每个磁石仅被访问一次。
Q4:如何暴力处理?
A4:块内全部磁石都枚举一遍,将两个条件均合法的磁石加入队列。但注意,暴力处理会影响到整块处理。因为假设暴力处理时push了一个磁石,但这时对应块的标记并没有+ +。到时候整块处理的时候,这个磁石仍会被枚举到,符合条件答案被统计了两次,怎么办呢?很简单,开个vis数组,每push一个磁石就将其标记。整块处理时如果已经打过标记了,块的位置标记直接+ +,不用统计答案。
#include <iostream>
#include <cstdio>
#include <queue>
#include <algorithm>
#include <cmath>
using namespace std;
#define int long long
const int N=250005;
const int T=1005;
struct Blok {int l,r,p,d;} blok[T];
struct A {int x,y,r,d,m,p;} a[N];
int x,y,r,n,p,siz,cnt,ans;
bool vis[N];
queue<A> que;
int read() {
int x=0,f=1; char c=getchar();
while(c<'0' || c>'9') {if(c=='-') f=-1; c=getchar();}
while(c>='0' && c<='9') {x=x*10+c-'0'; c=getchar();}
return x*f;
}
int sq(int v) {return v*v;}
bool cmp1(A u,A v) {return u.d<v.d;}
bool cmp2(A u,A v) {return u.m<v.m;}
int get(int v) {
int l=1,r=cnt,res;
while(l<=r) {
int mid=l+r>>1;
if(blok[mid].d<=v) res=mid,l=mid+1;
else r=mid-1;
}
return res;
}
void build() {
siz=sqrt(n),cnt=n/siz;
if(n%siz) cnt++;
for(int i=1;i<=cnt;i++) {
int l=(i-1)*siz+1,r=min(n,i*siz);
blok[i]={l,r,l,a[l].d},sort(a+l,a+1+r,cmp2);
}
}
void solve(int r,int v1,int v2) {
//暴力查第r块,统一处理r-1块
for(int i=1;i<=r-1;i++)
while(blok[i].p<=blok[i].r && a[blok[i].p].m<=v1) {
if(!vis[blok[i].p]) {
ans++,que.push(a[blok[i].p]);
vis[blok[i].p]=1;
}
blok[i].p++;
}
for(int i=blok[r].l;i<=blok[r].r;i++)
if(!vis[i] && a[i].m<=v1 && a[i].d<=v2)
ans++,vis[i]=1,que.push(a[i]);
}
signed main()
{
cin>>x>>y>>p>>r>>n,r=sq(r);
for(int i=1;i<=n;i++) {
a[i].x=read(),a[i].y=read(),a[i].m=read();
a[i].p=read(),a[i].r=read(),a[i].r=sq(a[i].r);
a[i].d=sq(a[i].x-x)+sq(a[i].y-y);
}
sort(a+1,a+1+n,cmp1);
build(),que.push((A){x,y,r,0,0,p});
while(que.size()) {
A x=que.front(); que.pop();
int r=get(x.r);
solve(r,x.p,x.r);
}
cout<<ans<<endl;
return 0;
}