• CH #46A


    题目链接:传送门

    描述
    在一片广袤无垠的原野上,散落着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)。

    样例输入
    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
    数据范围与约定
    对于30%的数据,1<=N<=1000。
    对于100%的数据,1<=N<=250000,-10^9<=x,y<=10^9,1<=m,p,r<=10^9。

    题解:

    首先,假设手上有若干块磁铁,我要让我吸到的磁铁尽可能多,显然最简单的办法就是把手上的磁铁都拿来吸吸看,把能吸到的都吸过来,

    所以可以用类似于BFS的形式,对手头的磁铁用一个队列维护,取出队头磁铁尝试吸引,把能吸到的磁铁都入队,反复如此知道队列为空,即可知道已经吸到了所有能被吸过来的磁铁。

    由于考虑一个磁铁能不能被吸引过来,要看满足两个条件与否:

      1、距离 ≤ 吸引半径

      2、手头磁铁的磁力 ≥ 被吸引的磁铁的质量

    对平面上所有磁铁按照与我的距离从近到远进行升序排序,分成 $T$ 块,显然每个分块内含有 $O(frac{N}{T})$ 个磁铁,再对每个分块内的磁铁按照质量升序排序,

    那么,对于当前的查询 $Q(p,r)$,对于吸引半径 $r$,必然存在一个整数 $k$,满足:

      1、第 $1 sim k$ 个分块中所有磁铁距离均小于等于 $r$;

      2、第 $k+2$ 个分块中所有磁铁距离均大于 $r$,不可能被吸引;

    分类讨论:

      ①对于满足距离条件的第 $1 sim k$ 个分块,分别从每个块的块头开始遍历,直到某一个磁铁质量大于磁力 $p$ 时就停止,同时将该磁铁设为新的块头;

      ②对于不确定是否满足距离条件的第 $k+1$ 个分块,暴力枚举块内所有磁铁,判断是否能被吸引,能吸引的就取走即可。

    时间复杂度:

      对于①,对于某一个分块,由于其中每个磁铁都只会被取走一次,均摊复杂度为 $O(1)$,一次询问最多处理 $O(T)$ 个分块,因此每次询问时间复杂度 $O(T)$;

      对于②,每次询问需要 $O(frac{N}{T})$ 的暴力枚举。

      ①和②合起来就是 $O(T+frac{N}{T})$,显然取 $T= sqrt N$ 时最小,为 $O(sqrt N)$;又最多有 $O(N)$ 次询问,总时间复杂度 $O(N sqrt N)$。

    AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=250000+10;
    const int maxt=500+10;
    
    struct Mg
    {
        ll x,y,m,p,r;
        ll d;
    }mg[maxn];
    inline ll sqr(ll x){return x*x;}
    inline ll dist(Mg a,Mg b){return sqr(a.x-b.x)+sqr(a.y-b.y);}
    bool cmp1(Mg a,Mg b){return a.d<b.d;}
    bool cmp2(Mg a,Mg b){return a.m<b.m;}
    
    int n;
    int len,tot;
    int L[maxt],R[maxt];
    ll maxd[maxt];
    int vis[maxn];
    
    int main()
    {
        cin>>mg[0].x>>mg[0].y>>mg[0].p>>mg[0].r>>n;
        mg[0].r*=mg[0].r;
        for(int i=1;i<=n;i++)
        {
            scanf("%lld%lld%lld%lld%lld",&mg[i].x,&mg[i].y,&mg[i].m,&mg[i].p,&mg[i].r);
            mg[i].r*=mg[i].r;
            mg[i].d=dist(mg[i],mg[0]);
        }
        sort(mg+1,mg+n+1,cmp1);
    
        len=sqrt(n);
        tot=0;
        for(int i=1;i<=n;i+=len)
        {
            tot++;
            L[tot]=i,R[tot]=min(i+len-1,n);
            maxd[tot]=mg[R[tot]].d;
            sort(mg+L[tot],mg+R[tot]+1,cmp2);
        }
    
        int ans=0;
        memset(vis,0,sizeof(vis));
        queue<int> q;
        q.push(0);
        vis[0]=1;
        while(!q.empty())
        {
            Mg &now=mg[q.front()]; q.pop(); ans++;
            for(int i=1;i<=tot;i++)
            {
                if(now.r>=maxd[i])
                {
                    for(int &j=L[i];j<=R[i] && now.p>=mg[j].m;j++)
                    {
                        if(vis[j]) continue;
                        q.push(j);
                        vis[j]=1;
                    }
                }
                else
                {
                    for(int j=L[i];j<=R[i];j++)
                    {
                        if(vis[j]) continue;
                        if(now.r>=mg[j].d && now.p>=mg[j].m)
                        {
                            q.push(j);
                            vis[j]=1;
                        }
                    }
                    break;
                }
    
            }
        }
    
        cout<<ans-1<<endl;
    }
  • 相关阅读:
    Linux上面执行 Windows 命令(比如 重启服务)的简单方法
    Linux 通过cksum 来判断文件是否是相同
    Linux 根据端口快速停止服务并启动的办法
    Delphi控件开发浅入深出(三)
    Delphi 资源文件( .res)
    C++中模块(Dll)对外暴露接口的方式
    delphi Align属性
    cport串口控件的应用
    两款工控控件对比评测:Iocomp和ProEssentials
    Android 将ARGB图片转换为灰度图
  • 原文地址:https://www.cnblogs.com/dilthey/p/9789584.html
Copyright © 2020-2023  润新知