[GDKOI模拟2016.01.26][JZOJ4218]补给站

题目大意

平面上有两个圆,坐标分别为 (xa,ya) (xb,yb) ,还有 n 个点,坐标分别为 (xi,yi)
q 个询问,每次给出两个圆各自半径 r1 r2 。要求输出有多少个点被至少一个圆覆盖(圆周也算在内)。

本题所有数字都为整数。
1n200000,1q100000,100000x,y100000,0r300000

题目分析

一个点 (x,y) 在半径为 r 、圆心为 (x0,y0) 的圆内(或圆上)当且仅当:

(x0x)2+(y0y)2r2

在题目中圆心和点的位置都是在询问前知道的,于是我们提前计算每个点与两个圆心不等式左边式子的值,也就是到圆心距离的平方,用这两个值作为新的关键字。
那么给出 r 之后,小于等于 r2 的关键字个数就是被这个圆覆盖的点的个数。
如果是一个圆,那我们直接快排二分就行了,但是这里是两个圆,答案为两个圆各自覆盖的点的个数和减去同时覆盖的点的个数。
怎么求同时满足两个小于等于的约束的点的个数呢?我们将两个新的关键字( xn yn )作为每个点的坐标,那么这个约束就可以看成平面中一个小平面 [0...xn][0...yn] 内点的个数。
显然我们可以对其中一维排序,另一位离散化之后在数据结构中插值。显然这里选用主席树是最好的。
时间复杂度 O((n+q)logn2) ,空间复杂度 O(nlogn2)

当然,这是这题对询问在线的做法,如果通过对询问离线的方法,我们可以用更加简单的树状数组等数据结构来做,空间复杂度可以降为 O(n) 。个人认为主席树代码复杂度并不大,换离线方法也不会好到哪里去,于是在这里不讨论离线做法。

代码实现

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cctype>

using namespace std;

typedef long long LL;

int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (!isdigit(ch))
    {
        if (ch=='-')
            f=-1;
        ch=getchar();
    }
    while (isdigit(ch))
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}

const int N=200005;
const int S=6000005;

struct P
{
    int xn,yn;
    LL x,y;
    P (LL x0=0,LL y0=0){x=x0,y=y0;}
}p[N+1],A,B;

P operator-(P a,P b)
{
    return P(a.x-b.x,a.y-b.y);
}

struct D
{
    LL v;
    int id,rank;
}s[2][N+1];

bool operator<(D a,D b)
{
    return a.v<b.v;
}

int n,m,cnt1,cnt2;

struct Chairman_Tree
{
    int son[S+1][2],size[S+1];
    int root[N+1];
    int tot;

    void init()
    {
        memset(son,0,sizeof son);
        memset(size,0,sizeof size);
        memset(root,0,sizeof root);
        tot=0;
        root[0]=newnode();
    }

    int newnode()
    {
        son[tot][0]=son[tot][1]=size[tot]=0;
        return tot++;
    }

    void insert(int &rt,int rt0,int l,int r,int x)
    {
        rt=newnode();
        son[rt][0]=son[rt0][0];
        son[rt][1]=son[rt0][1];
        size[rt]=size[rt0]+1;
        if (l==r)
            return;
        int mid=l+r>>1;
        if (x<=mid)
            insert(son[rt][0],son[rt0][0],l,mid,x);
        else
            insert(son[rt][1],son[rt0][1],mid+1,r,x);
    }

    int query(int rt,int st,int en,int l,int r)
    {
        if (!rt)
            return 0;
        if (st==l&&en==r)
            return size[rt];
        int mid=l+r>>1;
        if (en<=mid)
            return query(son[rt][0],st,en,l,mid);
        else
            if (mid+1<=st)
                return query(son[rt][1],st,en,mid+1,r);
            else
                return query(son[rt][0],st,mid,l,mid)+query(son[rt][1],mid+1,en,mid+1,r);
    }
}t;

int main()
{
    freopen("supply.in","r",stdin);
    freopen("supply.out","w",stdout);
    n=read(),m=read();
    A.x=read(),A.y=read(),B.x=read(),B.y=read();
    for (int i=1;i<=n;i++)
        p[i].x=read(),p[i].y=read();
    for (int i=1;i<=n;i++)
    {
        P pa=p[i]-A,pb=p[i]-B;
        s[0][i].v=pa.x*pa.x+pa.y*pa.y,s[1][i].v=pb.x*pb.x+pb.y*pb.y;
        s[0][i].id=i,s[1][i].id=i;
    }
    sort(s[0]+1,s[0]+1+n);
    sort(s[1]+1,s[1]+1+n);
    s[0][0].v=-1;
    cnt1=0;
    for (int i=1;i<=n;i++)
    {
        p[s[0][i].id].xn=(s[0][i].v!=s[0][i-1].v)?++cnt1:cnt1;
        s[0][i].rank=cnt1;
    }
    s[1][0].v=-1;
    cnt2=0;
    for (int i=1;i<=n;i++)
    {
        p[s[1][i].id].yn=(s[1][i].v!=s[1][i-1].v)?++cnt2:cnt2;
        s[1][i].rank=cnt2;
    }
    t.init();
    for (int i=1;i<=n;i++)
        t.insert(t.root[i],t.root[i-1],1,cnt2,p[s[0][i].id].yn);
    for (int i=1,r1,r2;i<=m;i++)
    {
        r1=read(),r2=read();
        LL s1=(LL)r1*r1,s2=(LL)r2*r2;
        int l=1,r=n,p1=0;
        while (l<=r)
        {
            int mid=l+r>>1;
            if (s[0][mid].v<=s1)
            {
                p1=mid;
                l=mid+1;
            }
            else
                r=mid-1;
        }
        int p2=0;
        l=1,r=n;
        while (l<=r)
        {
            int mid=l+r>>1;
            if (s[1][mid].v<=s2)
            {
                p2=mid;
                l=mid+1;
            }
            else
                r=mid-1;
        }
        p2=s[1][p2].rank;
        int ans1=p2?t.query(t.root[n],1,p2,1,cnt2):0;
        int ans2=t.query(t.root[p1],1,cnt2,1,cnt2);
        int ans3=p2?t.query(t.root[p1],1,p2,1,cnt2):0;
        ans1=ans1+ans2-ans3;
        printf("%d\n",ans1);
    }
    fclose(stdin);
    fclose(stdout);
    return 0;
}

你可能感兴趣的:(排序,离散化,OI,主席树,GDKOI)