首页 > 其他 > 详细

[被迫营业2-1]分块解决双维度查询问题

时间:2021-05-04 23:42:46      阅读:23      评论:0      收藏:0      [点我收藏+]

Problem

ACWing 250 磁力块

技术分享图片

How to solve it?

考虑BFS,先将\(L\)加入队列,然后每次将其能吸引的磁石加入队列\(_{[1]}\),最后进入过队列的数\(-1\)(因为答案不包含最初携带的磁石\(L\))即为所求。

考虑如何找到哪些能吸引的磁石?

Solution 1 \(\mathcal{O(n)}\)

枚举每个磁石,判断其能不能被吸引。

# include <bits/stdc++.h>
using namespace std;

typedef long long ll;

const int N = 250005;

struct node
{
    ll x,y,m,p,r;
    node() {}
    node(ll _x,ll _y,ll _m,ll _p,ll _r) : x(_x),y(_y),m(_m),p(_p),r(_r) {}
}a[N],L;

int n;

bool vis[N];

double dist(node a,node b)
{
    return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}

bool check(node a,node b) // a xi b
{
    if(double(a.r) < dist(a,b)) return 0;
    if(b.m > a.p) return 0;
    return 1;
}

void Read(struct node &x,bool opt = 0)
{
    if(opt) // read L
    {
        scanf("%lld%lld%lld%lld",&x.x,&x.y,&x.p,&x.r);
    }
    else scanf("%lld%lld%lld%lld%lld",&x.x,&x.y,&x.m,&x.p,&x.r);
    return;
}

void bfs(void)
{
    queue <node> q;
    q.push(L);
    while(!q.empty())
    {
        node x = q.front();q.pop();
        for(int i = 1; i <= n; i++)
        {
            if(vis[i]) continue;
            if(check(x,a[i])) 
            {
                q.push(node(x.x,x.y,a[i].m,a[i].p,a[i].r));
                vis[i] = 1;
            }
        }
    }
    return;
}

int main(void)
{
    Read(L,1);
    scanf("%d",&n);
    for(int i = 1; i <= n; i++) 
    {
        Read(a[i]);
    }
    bfs();
    int total = 0;
    for(int i = 1; i <= n; i++) total += vis[i];
    printf("%d\n",total);
    return 0;
}

Solution 2 \(\mathcal{O}(\sqrt{n})\)

预处理:将磁石按照质量从小到大排序,并分块,长度为\(\sqrt{n}\)

(下文为了方便,设块长度为\(len\),数量为\(num\)

对于每个块,块内按照与\((x_0,y_0)\)的距离从小到大排序。

预处理出每个块中磁石的最大质量\(\text{Max-m}\)

对于每次询问:

令队头磁石的磁力为\(p\)

已经有一个分界值\(k\),使得:

\(\forall1 \le i \le k,\text{Max-m}[i] \le p\).

\(\forall k < i \le num,\text{Max-m}[i] > p\)

Lemma 1. 此时能被吸引的磁石只会在\([1,\min(k + 1,num)]\)的块中。

Proof:

\(k + 1 > num\),引理显然成立。

\(k + 1 \le num\)

显然此时\(\text{Max-m}[k + 1] > p\),那么说明在\(k + 1\)块中一定有质量大于\(p\)的磁块,而这个磁块被分到了\(k + 1\)块中,说明对于任何在\(k + 1\)块之后的所有磁石都要大于它。那么显然是无法吸引的。证毕。

对于\(1 \le i \le k\)的块,因为块中已经按照距离排序,所以我们每次都只需要从开头开始找,若能吸引则加入队列,并从块中删除,防止重复入队。

对于块\(k + 1\),暴力枚举即可。

# include <bits/stdc++.h>
using namespace std;

# define int long long

# define y0 Iakioi
# define x0 Youakioi

const int N = 250005;

int n;
int x0,y0;
int num,len;
int Left[1000],Right[1000];
int Max_m[1000];

int total = 0;

struct node
{
    int dis,m,p,r;
    node() {}
    node(int _dis,int _m,int _p,int _r) : dis(_dis),m(_m),p(_p),r(_r) {}
}a[N],L;

bool vis[N];

bool compare(const struct node &x,const struct node &y)
{
    return x.m < y.m;
}

bool compare2(const struct node &x,const struct node &y)
{
    return x.dis < y.dis;
}

void init(void)
{
    sort(a + 1, a + n + 1, compare);
    len = sqrt(n);
    num = (n - 1) / len + 1;
    for(int i = 1; i <= num; i++)
    {
        Left[i] = (i - 1) * len + 1;
        Right[i] = min(i * len,n);
        Max_m[i] = a[Right[i]].m;
        sort(a + Left[i],a + Right[i] + 1, compare2);
    }
    return;
}

void bfs(void)
{
    queue <node> q;
    q.push(L);
    while(!q.empty())
    {
        node x = q.front();q.pop();
        for(int i = 1; i <= num; i++)
        {
            if(Max_m[i] > x.p)
            {
                for(int j = Left[i]; j <= Right[i]; j++)
                {
                    if(!vis[j] && a[j].dis <= x.r && a[j].m <= x.p)
                    {
                        q.push(a[j]);
                        ++total;
                        vis[j] = 1;
                    }
                    else if(a[j].dis > x.r) break;
                }
                break;
            }
            else
            {
                while(Left[i] <= Right[i] && a[Left[i]].dis <= x.r)
                {
                    if(!vis[Left[i]])
                    {
                        q.push(a[Left[i]]);
                        ++total;
                    }
                    ++Left[i];
                }
            }
        }
    }
    return;
}

signed main(void)
{
    scanf("%lld%lld%lld%lld",&x0,&y0,&L.p,&L.r);
    L.r *= L.r;
    L.m = L.dis = 0;
    scanf("%lld",&n);
    for(int i = 1; i <= n; i++)
    {
        int x,y;
        scanf("%lld%lld%lld%lld%lld",&x,&y,&a[i].m,&a[i].p,&a[i].r);
        a[i].dis = (x - x0) * (x - x0) + (y - y0) * (y - y0);
        a[i].r *= a[i].r;
    }   
    init();
    bfs();
    printf("%lld\n",total);
    return 0;
}

Solution 2 - Faster

在第\(k + 1\)块中,如果遇到\(dis > x.r\)的,可以直接break.

(上面已经写了)

[被迫营业2-1]分块解决双维度查询问题

原文:https://www.cnblogs.com/luyiming123blog/p/14730192.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!