【2025.10.14NOIP】补给站题解

题目描述

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

输入

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

输出

输出的第1至m行包含1个整数,表示其所对应的询问的答案。

样例输入 复制
4 2
-1 0 2 0
0 0
1 1
2 2
0 2
3 1
1 1
样例输出 复制
3
1
【样例说明】
对于第一个询问,第1,2,4个休息点都能受到补给。
对于第二个询问,只有第1个休息点能受到补给。
数据范围限制

对于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]。

思路

容斥原理,a|b=a+b-a&b,二分查找a,b,线段树维护a&b即可。

代码见下

#include<bits/stdc++.h>
using namespace std;
long long n,m,ax,ay,bx,by,lo=3e5,x[200005],y[200005],re1[200005],re2[200005],ef[300005],es[200005],r1,r2,l,r,md,mid,md2;
struct one{
	long long a,b,c,d;
}re[200005],rr[200005];
bool cmp(one a1,one b1){
	if(a1.a!=b1.a){
		return a1.a<b1.a;
	}
	else{
		return a1.b<b1.b;
	} 
}
bool cmp2(one a1,one b1){
	return a1.c<b1.c;
}
long long abc(long long a1,long long b1,long long c1){
	long long o1=sqrt((a1-ax)*(a1-ax)+(b1-ay)*(b1-ay)),o2=sqrt((a1-bx)*(a1-bx)+(b1-by)*(b1-by));
	if(o1*o1<(a1-ax)*(a1-ax)+(b1-ay)*(b1-ay)){
		o1++;
	}
	if(o2*o2<(a1-bx)*(a1-bx)+(b1-by)*(b1-by)){
		o2++;
	}
	if(c1==1){
		return o1;
	}
	else{
		return o2;
	}
}
long long te[4000006],lz[4000006],siz[4000006],xx;
void alz(int a1,long long v){
	te[a1]+=siz[a1]*v;
	lz[a1]+=v;
}//a1打标记值v
void dow(int a1){
	alz(a1*2,lz[a1]);
	alz(a1*2+1,lz[a1]);
	lz[a1]=0;
}//a1下发标记至儿子
void bu(int a1,int l,int r){
	siz[a1]=r-l+1;
	if(l==r){
		te[a1]=0;
		return ;
	}
	int mid=(l+r)/2;
	bu(a1*2,l,mid);
	bu(a1*2+1,mid+1,r);
	te[a1]=te[a1*2]+te[a1*2+1];
	return ;
}//l~r建树节点a1
void ci(int a1,int l,int r,int x,int y,long long v){
	if(l>=x&&r<=y){
		alz(a1,v);
		return ;
	}
	int mid=(l+r)/2;
	dow(a1);
	if(x<=mid){
		ci(a1*2,l,mid,x,y,v);
	}
	if(mid+1<=y){
		ci(a1*2+1,mid+1,r,x,y,v);
	}
	te[a1]=te[a1*2]+te[a1*2+1];
	return ;
}//x~y区间加v至l~r区间a1点
long long co(int a1,int l,int r,int x,int y){
	if(x<=l&&r<=y){
		return te[a1];
	}
	int mid=(l+r)/2;
	long long dbdb=0;
	dow(a1);
	if(mid>=x){
		dbdb+=co(a1*2,l,mid,x,y);
	}
	if(mid+1<=y){
		dbdb+=co(a1*2+1,mid+1,r,x,y);
	}
	return dbdb;
}//x~y区间问至l~r区间a1点	
int main(){
//	freopen("08080.in","r",stdin);
//	freopen("12121.out","w",stdout);
	cin>>n>>m;
	cin>>ax>>ay>>bx>>by;
	for(int i=1;i<=n;i++){
		cin>>x[i]>>y[i];
		re[i].a=abc(x[i],y[i],1);
		re[i].b=abc(x[i],y[i],2);
		re1[i]=abc(x[i],y[i],1);
		re2[i]=abc(x[i],y[i],2);
	}
	sort(re+1,re+n+1,cmp);
	sort(re1+1,re1+n+1);
	sort(re2+1,re2+n+1);
	for(int i=1;i<=m;i++){
		cin>>rr[i].a>>rr[i].b;
		rr[i].c=i;
	}
	sort(rr+1,rr+m+1,cmp);
	bu(1,0,lo);
	for(int i=1;i<=m;i++){
		r1=rr[i].a;
		r2=rr[i].b;
		l=1;
		r=n;
		md=0;
		while(l<=r){
			mid=(l+r)/2;
			if(re1[mid]<=r1){
				md=max(md,mid);
				l=mid+1;
			}
			else{
				r=mid-1;
			}
		}
		l=1;
		r=n;
		md2=0;
		while(l<=r){
			mid=(l+r)/2;
			if(re2[mid]<=r2){
				md2=max(md2,mid);
				l=mid+1;
			}
			else{
				r=mid-1;
			}
		}
		es[i]=md;
		for(int j=es[i-1]+1;j<=es[i];j++){
			ci(1,0,lo,re[j].b,re[j].b,1);
		}
		rr[i].d=md+md2-co(1,0,lo,0,r2);
	}
	sort(rr+1,rr+m+1,cmp2);
	for(int i=1;i<=m;i++){
		cout<<rr[i].d<<endl;
	}
	return 0;
}

posted @ 2025-10-14 08:43  bz02_2023f2  阅读(8)  评论(0)    收藏  举报  来源