AcWing 250 磁力快(分块)

题目传送门

在一片广袤无垠的原野上,散落着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)处吸引更多的磁石。
小取酒想知道,他最多能获得多少块磁石呢?

输入格式

第一行五个整数x0,y0,pL,rL,N,表示小取酒所在的位置,磁石L磁力、吸引半径和原野上散落磁石的个数。
接下来N行每行五个整数x,y,m,p,r,描述一块磁石的性质。

输出格式

输出一个整数,表示最多可以获得的散落磁石个数(不包含最初携带的磁石L)。

数据范围

\(1≤N≤250000, −10^9≤x,y≤10^9, 1≤m,p,r≤10^9\)

输入样例:

0 0 5 10 5
5 4 7 11 5
-7 1 4 7 8
0 2 13 5 6
2 -3 9 3 4
13 5 1 9 9

输出样例:

3

分析

将磁石按照质量排序,分成t块,然后对于一个磁石一定有一个k,使得前k-1块所有的磁石的质量都小于它的磁力,k+1块之后的磁石质量都比它磁力大,而对块内按照距离排序之后,只需要挨个扫就可以了,扫过的标记、加入队列,调整区间。但对于第k块,因为不是所有的质量都小于当前磁块的磁力,所以要全部扫一遍,直到距离大于磁力半径,该块不能被调整区间

#include <bits/stdc++.h>
using namespace std;
const int N = 250010;
struct node{
    int x,y,m,p,r;
    double dis;
}a[N];
int x0,y,n;
int L[N],R[N],v[N];
double S(int x){return 1.0*x * x;}
bool cmp(node a,node b){
    return a.m < b.m;
}
bool cmp2(node a,node b){
    return a.dis < b.dis;
}
int main(){
    scanf("%d%d%d%d%d",&x0,&y,&a[0].p,&a[0].r,&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d%d%d%d",&a[i].x,&a[i].y,&a[i].m,&a[i].p,&a[i].r);
        a[i].dis = sqrt(S(a[i].x-x0)+S(a[i].y-y));
    }
    int t = sqrt(n);
    for(int i=1;i<=t;i++){
        L[i] = (i-1) * t + 1;
        R[i] = i * t;
    }
    if(R[t] < n){
        t ++;L[t] = R[t-1] + 1;R[t] =n;
    }
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=t;i++){
        sort(a+L[i],a+R[i]+1,cmp2);
    }
    int res = 0;
    queue<int> q;
    q.push(0);
    while(!q.empty()){
        int u = q.front();q.pop();
        res++;
        int r = a[u].r,p = a[u].p;
        bool flag = true;//标记是否为前k-1块
        for(int i=1;i<=t;i++){
            if(!flag)break;
            int j = L[i];
            for(;j<=R[i];j++){
                if(v[j])continue;
                if(p >= a[j].m){
                    if(r >= a[j].dis){
                        v[j] = 1;q.push(j);
                    }else{
                        break;
                    }
                }else{
                    flag = false;continue;//是第k块
                }
            }
            if(flag)L[i] = j;//不是第k块就调整左坐标点
        }
    }
    printf("%d\n",res-1);//减去原始的磁石
    return 0;
}
posted @ 2019-08-06 17:32  kpole  阅读(234)  评论(0编辑  收藏  举报