磁力块——分块

本文介绍了一种基于分块思想的高效算法,用于解决磁石收集问题。算法通过将大区间划分为小区间,对距离和质量进行排序,利用队列实现磁石的递归收集,有效提升了算法效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意
有 N 块磁石。一个人手中的石头可以通过磁力吸引地上的其它石头,而地上的石头不会互相吸引。人自己的坐标设为(x0,y0)(x0,y0)。地上第 i 块石头的坐标为 (xi,yi)(xi,yi),质量为 mi,磁力为 pi,吸引半 径为 ri。
人站在 (x0,y0)(x0,y0) 原地不动,不断地从已经获得的石头中拿起一块, 去吸引其它石头。若一块石头的“质量,与人的距离”分别不大于“人正在拿着的石头的磁力、吸引半径”,则该石头会被吸引到 (x0,y0)(x0,y0) 处。问最后能获得多少块石头?

分析
整体思路:
对于可以吸引的磁石需要有两个条件,第一,距离必须在半径范围之内,第二,所吸引的磁石的质量必须小于手中磁石的磁力。只有这样才可吸引磁石。我们首先可以想到的对于距离我们进行排序,然后遍历距离范围之内的磁石,如果质量小于磁力那么就ans++,这种想法没有错误,但是对于数据范围这么大,就没法AC了。为了解决这一问题我们引入 分块这一思想——把一个较大的区间分成若干个小的区间,对于大的区间我们按照距离进行排序,对于小的区间我们按照质量进行排序,并用一个数组Maxdist【】来记录这一个小区间的最大值。

详细思路看代码:

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

using namespace std;
typedef long long ll;
const ll maxn=250010;//分块大小sqrt(maxn)
struct Stone
{
    ll d,r,m,p;   //两点之间的距离平方d,防止出现小数错误。磁力范围 r ,注意存入的时候平方
}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;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值