【五校联考2day1】补给站

来源:互联网 发布:js history 编辑:程序博客网 时间:2024/05/17 08:47

Description

WYF为了保证他自己能够吃到足够多的牛排,来补充自己的脑力,所以他建了两个补给站,坐标分别为(ax,ay),(bx,by)。他有n个休息地点,第i个休息地点的坐标是(xi,yi)。每个补给站都有一个补给半径,当一个休息地点在以一个补给站为圆心,该补给站的补给半径为半径的圆中时(包括在圆周上),那个休息地点就会获得补给。现在有m个询问,每个询问会给出第一个补给站的补给半径r1和第二个补给站的补给半径r2,WYF想知道有多少个休息地点会得到补给。
Input

输入的第一行包含2个整数,n与m。
第二行包含4个整数,ax,ay,bx,by。
第3至n+2行包含2个整数x,y。
第n+3至n+m+2行包含两个整数r1,r2。

Output

输出的第1至m行包含1个整数,表示其所对应的询问的答案。
Data Constraint
对于30%的数据:n≤5000,m≤5000.
对于100%的数据:
n≤2*10^5,m≤10^5,ax,ay,bx,by,x,y∈[-100000,100000],r1,r2∈[0,300000]。

分析

这题并没有强制在线,所以有离线做法和在线做法两种。

1.离线做法

其实在此之前,我们可以发现对于一个询问(x,y).先预处理出一个点到两个补给站的距离(a,b)我们只需要用二分或者树状数组线段树求出1..x中有多少个a的个数加上1..y中b的个数,然后再减去有多少个同时满足x也同时满足y的数对的个数。而现在的关键是后面的同时满足的情况。
那么我们可以从一个满足的情况去求两个满足的情况,只用把1..x满足的情况放到树状数组或线段树里面求有多少个同时满足1..y的情况就可以了。而问题是我们每次将1..x放到树状数组里面,然后求完后又清空,这样太过浪费了,所以我们可以将询问排个序,排完序后每次再更新多出来的数——把比上次多出来的放到树状数组里面就可以了。

2.在线做法

我们也可以在上面的思路里面思考。
如何快速求出有多少个同时满足X也同时满足Y的数对的个数。
我们可以用平面直角坐标系里面表示点,那么横坐标表示的是x,纵坐标表示y,那么答案便是ans(X,Y)即在1..x中的1..y这怎么那么像主席树?没错,我们可以用主席树求出1..x中满足第二个距离在1..y中的答案。

在这里我讲讲我打第2种做法的故事。。
首先我听到学长说可以用主席树,于是很兴奋的拿来练手,但是没有仔细想用什么来表示第一维即1..x中的x表示什么(可以是排序后的标号,或者是我用的直接的x),而我直接用了x表示距离,所以在make()时(求主席树时)就比较麻烦(其实也不是很麻烦。。),那时我是这样的:

void make(ll &x,ll y,ll l,ll r){    if (!y) return;    if (!x) ++cnt,x=cnt;    if (l>r) return;    ll mid=(l+r)>>1;    sum[x]+=sum[y];    make(lef[x],lef[y],l,mid);    make(rig[x],rig[y],mid+1,r);}    for(int i=1;i<=n;i++) insert(root[a[i].x],root[a[i].x],1,ne,a[i].y);    for(int i=1;i<=now;i++)     if (!root[i])root[i]=root[i-1];else make(root[i],root[i-1],1,ne);

a[i]表示的是没有排过序的a数组。ne表示的是y经过离散化后的最大值。
关键是后面的那重循环,我就脑残了。。这样去更新是把每个线段树的节点都遍历了一次,这样的时间复杂度是最高的,相当于做n次线段树,先不说时间,空间就爆了,所以主席树才强调从上一次更新过来,这样的更新并不会太大,所以主席树的空间时间是可以。

后来我才意识到,我们可以也这样做,只不过我们再记录ce[i]表示x=i时的y的各种值,可以用c++里面的库vector
ps:这次用vector终于不爆各种runtime error了。。

这是关键代码    for(int i=1;i<=M*2;i++) ce[i].clear();    for(int i=1;i<=n;i++)     ce[a[i].x].push_back(a[i].y);    for(int i=1;i<=now;i++){        if (ce[i].empty()) root[i]=root[i-1];        else {            insert(root[i],root[i-1],1,ne,ce[i][0],0);            ll len=ce[i].size();            for(j=1;j<=len-1;j++)             insert(root[i],root[i],1,ne,ce[i][j],1);        }    }

还有最后大家不要打我——为了方便练习主席树,这里的代码便没有打在线,也就少了个二分而已,大家不要太在意,O(∩_∩)O~

完整代码

离线:

#include<iostream>#include<cstdio>#include<cstdlib>#include<cmath>#include<cstring>#include<algorithm>#define ll long long#define db doubleusing namespace std;const int N=300005;const int M=300005;ll re[N],d[N],n,m,ax,bx,by,ay,x,y,dat[N],t[N],num[N];struct nd{    ll x,y,u;}b[N],a[N];ll sqr(ll x){    return x*x;}bool cmp(nd x,nd y){    return x.x<y.x;}void insert(ll x,ll y){    while (x<=M){        t[x]+=y;x+=(x&(-x));    }}ll find(ll x){    ll ans=0;    while (x){        ans+=t[x];x-=(x&(-x));    }    return ans;}int main(){    scanf("%lld %lld",&n,&m);    scanf("%lld %lld %lld %lld",&ax,&ay,&bx,&by);    for(int i=1;i<=n;i++) {        scanf("%lld %lld",&x,&y);        a[i].x=ceil(sqrt(sqr(ax-x)+sqr(ay-y)));a[i].u=i;        a[i].y=ceil(sqrt(sqr(bx-x)+sqr(by-y)));        insert(a[i].y,1);    }    sort(a+1,a+n+1,cmp);    for(int i=1;i<=m;i++){        scanf("%lld %lld",&b[i].x,&b[i].y);        b[i].u=i;    }    sort(b+1,b+m+1,cmp);    ll l,r,mid,now,id;    now=0;id=1;    for(int i=1;i<=m;i++){        while (a[id].x<=b[i].x){            now++;insert(a[id].y,-1);id++;        }        dat[b[i].u]=now+find(b[i].y);    }    for(int i=1;i<=m;i++) printf("%lld\n",dat[i]);}

在线

#include<iostream>#include<cstdio>#include<cstdlib>#include<cmath>#include<cstring>#include<vector> #include<algorithm>#define ll long long#define db doubleusing namespace std;const int N=300005;const int M=300005;ll n,m,ax,bx,by,ay,x,y,tt,ne,cnt,tot,now,lef[M*30],rig[M*30],root[N+M],sum[M*30],j;vector<ll>ce[M*2];struct nd{    ll x,y;}b[N],a[N],c[M+M],d[N+M];ll sqr(ll x){    return x*x;}bool cmp(nd x,nd y){    return x.x<y.x;}void insert(ll &x,ll y,ll l,ll r,ll z,int op){    ++cnt,x=cnt;    lef[x]=lef[y];    rig[x]=rig[y];    sum[x]=sum[y]+1;    if (l==r) return;     ll mid=(l+r)>>1;    if (mid>=z) insert(lef[x],lef[y],l,mid,z,op);else insert(rig[x],rig[y],mid+1,r,z,op);}ll find(ll v,ll l,ll r,ll x,ll y){    if (v==0) return 0;    if (l==x&&r==y) return sum[v];    ll mid=(l+r)>>1;    if (mid>=y) return find(lef[v],l,mid,x,y);else    if (mid<x) return find(rig[v],mid+1,r,x,y);    else return find(lef[v],l,mid,x,mid)+find(rig[v],mid+1,r,mid+1,y);}int main(){    scanf("%lld %lld",&n,&m);    scanf("%lld %lld %lld %lld",&ax,&ay,&bx,&by);    for(int i=1;i<=n;i++) {        scanf("%lld %lld",&x,&y);        a[i].x=ceil(sqrt(sqr(ax-x)+sqr(ay-y)));        a[i].y=ceil(sqrt(sqr(bx-x)+sqr(by-y)));        c[++tot].x=a[i].x;c[tot].y=i;        d[++tt].x=a[i].y;d[tt].y=i;    }    for(int i=1;i<=m;i++){        scanf("%lld %lld",&b[i].x,&b[i].y);        c[++tot].x=b[i].x;c[tot].y=i+n;        d[++tt].x=b[i].y;d[tt].y=i+n;    }    sort(c+1,c+tot+1,cmp);    sort(d+1,d+tt+1,cmp);    c[0].x=-N;d[0].x=-N;    for(int i=1;i<=tot;i++){        if (c[i].x!=c[i-1].x) now++;        if (c[i].y<=n) a[c[i].y].x=now;else b[c[i].y-n].x=now;    }    for(int i=1;i<=tt;i++){        if (d[i].x!=d[i-1].x) ne++;        if (d[i].y<=n) a[d[i].y].y=ne;else b[d[i].y-n].y=ne;    }     for(int i=1;i<=M*2;i++) ce[i].clear();    for(int i=1;i<=n;i++)     ce[a[i].x].push_back(a[i].y);    for(int i=1;i<=now;i++){        if (ce[i].empty()) root[i]=root[i-1];        else {            insert(root[i],root[i-1],1,ne,ce[i][0],0);            ll len=ce[i].size();            for(j=1;j<=len-1;j++)             insert(root[i],root[i],1,ne,ce[i][j],1);        }    }    for(int i=1;i<=m;i++){        ll dat1=find(root[b[i].x],1,ne,1,ne);        ll dat2=find(root[now],1,ne,1,b[i].y);        ll dat3=find(root[b[i].x],1,ne,1,b[i].y);        printf("%lld\n",dat1+dat2-dat3);    }}
1 0
原创粉丝点击