磁力块——解题报告

题目链接:http://172.18.70.217/problem/16
题目大意:有 N 块磁石。一个人手中的石头可以通过磁力吸引地上的其它石头,而地上的石头不会互相吸引。人自己的坐标设为(x0,y0)(x0,y0)。地上第 i 块石头的坐标为 (xi,yi)(xi,yi),质量为 mi,磁力为 pi,吸引半 径为 ri。
人站在 (x0,y0)(x0,y0) 原地不动,不断地从已经获得的石头中拿起一块, 去吸引其它石头。若一块石头的“质量,与人的距离”分别不大于“人正在拿着的石头的磁力、吸引半径”,则该石头会被吸引到 (x0,y0)(x0,y0) 处。问最后能获得多少块石头?
解题思路:很容易想到的一个思路就是首先按照距离起点的距离进行排序。用一个队列存储我们得到的石头。我们可以在O(logn)的时间内,找到所有距离小于手上石头,但是我们发现,我们还要考虑质量和磁力之间的关系,但是质量并没有排序。我们自然而然的就想到了将整个序列分为若干小段,序列整体按照距离排序,方便距离的查找,但是小区间内部按照质量排序,方便找到对应距离后的质量的查找。这就是分块的思想,通常我们分为大体效果如图所示:
这里写图片描述
执行过程:
1.按照长度为√n分割出子区间,维护一个指针保存最靠前的没有被吸附的石头。
2.取出队首元素表示手上的石头,从小到大遍历区间。
3.如果区间尾部的元素的距离小于取出石头的吸附范围,从区间的指针开始遍历并入队同时移动指针,直到出现质量>磁力的情况出现为止。
4.如果区间尾部的元素的距离大于取出石头的吸附范围,从这个区间指针开始遍历到右端,将所有能被吸附的石头入队。
5.重复上述过程知道队列为空。
附上AC代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <cmath>

using namespace std;
typedef long long ll;
const ll maxn=250010;
struct Stone
{
    ll d,r,m,p;
}sto[maxn];
ll Maxdist[maxn],sx,sy;
ll lef[maxn],rig[maxn],n,x,y;
bool vis[maxn];
bool cmp_d(Stone a,Stone b)
{
    return a.d<b.d;
}
bool cmp_m(Stone a,Stone b)
{
    return a.m<b.m;
}
int main()
{
    scanf("%lld%lld%lld%lld%lld",&sx,&sy,&sto[0].p,&sto[0].r,&n);
    sto[0].r*=sto[0].r;
    for(ll i=1;i<=n;i++)
    {
        scanf("%lld%lld%lld%lld%lld",&x,&y,&sto[i].m,&sto[i].p,&sto[i].r);
        sto[i].r*=sto[i].r;
        sto[i].d=(sx-x)*(sx-x)+(sy-y)*(sy-y);
    }
    sort(sto+1,sto+n+1,cmp_d);
    ll tot=0;
    ll w=sqrt(n);
    for(ll i=1;i<=n;i+=w)
    {
        lef[++tot]=i;
        rig[tot]=min(n,i+w-1);
        Maxdist[tot]=sto[rig[tot]].d;
        sort(sto+lef[tot],sto+rig[tot]+1,cmp_m);
    }
    queue<ll> q;
    q.push(0);
    ll ans=-1;
    while(!q.empty())
    {
        ll u=q.front();
        ans++;
        q.pop();
        ll rad=sto[u].r;
        ll p=sto[u].p;
        for(ll i=1;i<=tot;++i)
        {
            if(Maxdist[i]>rad)
            {
                for(ll j=lef[i];j<=rig[i];j++)
                {
                    if(!vis[j] && sto[j].d<=rad && sto[j].m<=p)
                    {
                        q.push(j);
                        vis[j]=true;
                    }
                }
                break;
            }
            while(lef[i]<=rig[i] && sto[lef[i]].m<=p)
            {
                if(!vis[lef[i]])
                    q.push(lef[i]);
                ++lef[i];
            }
        }
    }
    printf("%lld",ans);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值