【五校联考2day1】补给站
2016-01-28 18:11
931 查看
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()时(求主席树时)就比较麻烦(其实也不是很麻烦。。),那时我是这样的:
[code] 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了。。
[code]这是关键代码 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~
完整代码
离线:[code]#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<algorithm> #define ll long long #define db double using namespace std; const int N=300005; const int M=300005; ll re ,d ,n,m,ax,bx,by,ay,x,y,dat ,t ,num ; struct nd{ ll x,y,u; }b ,a ; 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]); }
在线
[code]#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<vector> #include<algorithm> #define ll long long #define db double using 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 ,a ,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); } }
相关文章推荐
- Android Studio gradle的基本用法
- centos6.5Xen4.2安装
- 随机色
- embed 层级太高
- hdoj5510Bazinga【strstr+并查集】
- 几种常见的Shell
- Shell简介:什么是Shell,Shell命令的两种执行方式
- 数据结构——队列
- ios 建立私有的 pod 仓库
- CentOS搭建svn服务器支持https访问
- Linux下rsync设置+inotify设置文件同步
- Shell脚本语言与编译型语言的差异
- 通知栏的相关用法记录
- TableViewCell的复用出现数据重复的解决方法
- Android布局文件中的四种单位
- 135 js 高程6.1
- Jquery自定义扩展方法(一)
- 蓝桥杯基础练习 Huffuman树
- VC中,高精度休眠函数
- spark transform系列__cogroup