[GDKOI模拟2016.01.26][JZOJ4218]补给站
2016-01-26 16:26
281 查看
题目大意
平面上有两个圆,坐标分别为(xa,ya)、(xb,yb),还有n个点,坐标分别为(xi,yi)。有q个询问,每次给出两个圆各自半径r1和r2。要求输出有多少个点被至少一个圆覆盖(圆周也算在内)。
本题所有数字都为整数。
1≤n≤200000,1≤q≤100000,−100000≤x,y≤100000,0≤r≤300000
题目分析
一个点(x,y)在半径为r、圆心为(x0,y0)的圆内(或圆上)当且仅当:(x0−x)2+(y0−y)2≤r2
在题目中圆心和点的位置都是在询问前知道的,于是我们提前计算每个点与两个圆心不等式左边式子的值,也就是到圆心距离的平方,用这两个值作为新的关键字。
那么给出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 ,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; }
相关文章推荐
- 喝醉的酒鬼总能找到回家的路,喝醉的小鸟则可能永远也回不了家
- Volley解析中文字符
- 技术的争论--人决定技术
- punctuation
- android之播放视频
- Python3.4 tkinter,PIL图片转换(GUI)
- PagerSlidingTabStrip自定义设置
- vim快捷键
- JavaScript变量作用域
- Delphi XE7 android 实现的在线更新APP的一个程序
- 浅析Hibernate映射(一)——基本映射
- liunx 命令 文件内容查找
- .NET中的Queue和Stack
- Duilib初级控件扩展一例: 具有鼠标滚动消息的OptionUI
- [原] Jenkins Android 自动打包配置
- 重温JSP学习笔记--三大指令九大内置对象
- Android XML解析
- Android textView复制,popupwindow显示在文字上方
- 【浅谈JavaEE框架】Spring中@Autowired标签与@Resource标签的区别
- Linux基础知识题解答(三)