主席树处理二维数点问题 园丁的烦恼

link

大意:

二维坐标里有n个点,q次询问,每次询问给定一个矩形的左下角和右上角,问在这个矩形范围内有多少个点

思路

#include<bits/stdc++.h>
using namespace std;
#define ll int
#define IL inline
#define endl '\n'
const ll N=5e5+10;
const ll inf=1e7+2;
namespace FastIOT{
	const int bsz=1<<18;
	char bf[bsz],*hed,*tail;
	inline char gc(){if(hed==tail)tail=(hed=bf)+fread(bf,1,bsz,stdin);if(hed==tail)return 0;return *hed++;}
	template<typename T>IL void read(T &x){T f=1;x=0;char c=gc();for(;c>'9'||c<'0';c=gc())if(c=='-')f=-1;
	for(;c<='9'&&c>='0';c=gc())x=(x<<3)+(x<<1)+(c^48);x*=f;}
	template<typename T>IL void print(T x){if(x<0)putchar(45),x=-x;if(x>9)print(x/10);putchar(x%10+48);}
	template<typename T>IL void println(T x){print(x);putchar('\n');}
}
using namespace FastIOT;
ll n,m,a,b,c,d;
ll rt[N];
ll idx=0;//根节点数
struct ty
{
	ll ls,rs;
	ll sum;
}tr[N<<5];
struct point
{
	ll x,y;
}mas[N];
bool cmp(point a,point b)
{
	return a.x<b.x;
}
inline ll find_x(ll x)//找到l的范围
{
	ll l=1,r=n;
	while(l<=r)
	{
		ll mid=l+r>>1;
		if(mas[mid].x>=x) r=mid-1;
		else l=mid+1;
	}
	return r+1;
}
inline ll find_y(ll x)
{
	ll l=1,r=n;
	while(l<=r)
	{
		ll mid=l+r>>1;
		if(mas[mid].x<=x) l=mid+1;
		else r=mid-1; 
	} 
	return l-1;
}

inline void build(ll &p,ll l,ll r,ll v)
{
	tr[++idx]=tr[p];
	p=idx;
	tr[p].sum++;//该种类的数量+1
	if(l==r) return;
	ll mid=l+r>>1;
	if(v<=mid) build(tr[p].ls,l,mid,v);
	else build(tr[p].rs,mid+1,r,v);
} 
inline ll query(ll p,ll l,ll r,ll don,ll up)//节点p,p对应的区间[l,r],区间内在[don,up]之间的数的数量 
{
	if(!p) return 0;
	if(l>up||r<don) return 0;
	if(don<=l&&up>=r) return tr[p].sum;
	ll mid=l+r>>1;
	return query(tr[p].ls,l,mid,don,up)+query(tr[p].rs,mid+1,r,don,up);
}
void solve()
{
	read(n);read(m);
	for(int i=1;i<=n;++i) read(mas[i].x),read(mas[i].y);
	sort(mas+1,mas+1+n,cmp);
	for(int i=1;i<=n;++i)
	{
		rt[i]=rt[i-1];
		build(rt[i],0,inf,mas[i].y);
	}
	while(m--)
	{
		read(a);read(b);read(c);read(d);
		ll lk=find_x(a);ll rk=find_y(c);
		printf("%d\n",query(rt[rk],0,inf,b,d)-query(rt[lk-1],0,inf,b,d));
	}
}
int main()
{
//	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	solve();
	return 0;
}


经典二维数点。不妨先把所有点的坐标按照横坐标来排序,那么对于每次询问,我们可以很快获得点的序列范围,比如二分。然后考虑如何处理纵坐标上满足条件的点的数量。假设纵坐标的范围是[a,b],而我们确定的横坐标满足范围的点的序号范围是[c,d],那么我们按照顺序建立主席树,依次取出rt[c-1]和rt[d],每次获得一个区间和即可。主席树是满足可加性的,所以我们直接将两个结果减一下就好了。

code:

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值