杭电多校 zoto(莫队)

题目链接:https://acm.hdu.edu.cn/showproblem.php?pid=6959

题目描述:

您将获得一个数组fx。

对于每个i(1<=i<=n),我们使用XOY坐标平面中的点(i,fx[i])来描述它。

要求您回答m个查询,每个查询中将有一个矩形,您需要计算查询的矩形中有多少不同的y坐标(上面提到的点)。

输入格式:

第一行包含一个整数T(1<=T<=5),表示测试用例的数量。

对于每个测试用例,第一行中有两个整数n,m(1<=n<=100000,1<=m<=100000)。

然后一行包含n个整数fx[i](0<=fx[i]<=100000)

接下来的m行中的每一行都包含四个整数x0,y0,x1,y1(1<=x0<=x1<=n,0<=y0<=y1<=100000),这意味着矩阵的最左下坐标是(x0,y0),最右上坐标是(x1,y1)

输出格式:
每次查询输出一个整数

输入样例:

1

4 2

1 0 3 1

1 0 4 3

1 0 4 2

输出样例:

3

 题目大意  计算每个矩形中y坐标的种数

思路 用莫队去维护询问的区间,即x坐标,再把y坐标单独拎出来即可

具体代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int T,n,m,size,bnum; 
int a[maxn],num[maxn],sum[maxn],ans[maxn],belong[maxn];
struct node{
	int x1,x2,y1,y2,id;
}q[maxn];
bool cmp(node a,node b){
	return (belong[a.x1]^belong[b.x1]) ? belong[a.x1]<belong[b.x1] : ((belong[a.x1]&1) ? a.x2<b.x2 : a.x2>b.x2);
} //如果左端点在不同的区间,则按照左端点从小到大排序;否则如果左端点在同一奇数块就按照右端点的升序排序否,则降序排序 
void add(int x){//添加一个数
	if(!num[x]++) //如果以前没有这个数就加一 
	sum[x/size]++;
}
void dec(int x){//删除一个数
	if(!--num[x])//如果删掉这个数后,这个数的个数为0,减一 
	sum[x/size]--;
}
int calc(int x){//前缀和 找y坐标
	int now=0;
	for(int i=0;i<x/size;i++) now+=sum[i];
	for(int i=(x/size)*size;i<=x;i++) 
		if(num[i]) now+=1;//个数加一 
		//now+=(num[i]>=1);
	return now;
}
int main(){
	scanf("%d",&T);
	while(T--){
		memset(sum,0,sizeof(sum));//初始化 
		memset(num,0,sizeof(num)); 
		scanf("%d %d",&n,&m);
		size=313;//分块 
		bnum = ceil((double)n/size);//ceil函数是向上取整,它返回的是大于或等于函数的最小整数 
		for(int i=1;i<=bnum;i++){
			for(int j=(i-1)*size+1;j<=i*size;j++){
				belong[j]=i;//存每个块的左端点
			}
		}
		for (int i=1;i<=n;i++) scanf("%d",&a[i]);//a[i]相当于y坐标 
        for (int i=1;i<=m;i++){
        	scanf("%d %d %d %d",&q[i].x1,&q[i].y1,&q[i].x2,&q[i].y2);
			q[i].id=i;//储存下标 
		}
		sort(q+1,q+1+m,cmp); //按照区间排序 
		int l=1,r=0;
		for(int i=1;i<=m;i++){
			int ql=q[i].x1,qr=q[i].x2;
			while(l<ql) dec(a[l++]);
			while(l>ql) add(a[--l]);
			while(r<qr) add(a[++r]);
			while(r>qr) dec(a[r--]);
			ans[q[i].id]=calc(q[i].y2)-calc(q[i].y1-1);//求区间内的y坐标个数
		} 
		for(int i=1;i<=m;i++)
			printf("%d\n",ans[i]);
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值