【五校联考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); }}
- 【五校联考2day1】补给站
- 【五校联考2day1】补给站
- JZOJ 4218 【五校联考2day1】补给站
- 【五校联考2day1】池塘
- JZOJ 4219 【五校联考2day1】池塘
- 【五校联考6day1】c
- 【五校联考5day1】登山
- 【宝藏】题解(五校联考3day1)
- jzoj 4243. 【五校联考6day1】c 分块
- jzoj4243 【五校联考6day1】c 分块
- 【五校联考1day1】已经没有什么好害怕的了
- jzoj4210. 【五校联考1day1】我才不是萝莉控呢(哈夫曼树)
- noip2015五校联考2总结
- 关于五校联考
- 【五校联考】集体照
- 补给站
- 补给站
- 联考1day1总结
- js和jq的dom操作&&append的元素绑定事件失效
- Java读取properties文件及相关问题总结
- Android 5.0学习之定义阴影
- Unix 中进程间的通信
- 程序员眼中的英文单词是这样的
- 【五校联考2day1】补给站
- SIFT特征提取分析
- 谈谈自己学IT的感悟吧
- Android开发——思路源泉
- 说外语为何让我们幸运?
- 生产环境下JAVA进程高CPU占用故障排查
- Android滑动冲突
- Android动画机制全解析
- 蓝桥杯 - 大小写转换(水题)