NOIP2023模拟2联测23 分神

news2025/1/13 15:28:56

题目大意

n n n个矩形,每个矩形的四条边都平行于坐标轴。对于一个矩形,它的左下角坐标为 ( x 1 , y 1 ) (x_1,y_1) (x1,y1),右上角坐标为 ( x 2 , y 2 ) (x_2,y_2) (x2,y2),包含了所有满足 x 1 ≤ x ≤ x 2 , y 1 ≤ y ≤ y 2 x_1\leq x\leq x_2,y_1\leq y\leq y_2 x1xx2,y1yy2的点 ( x , y ) (x,y) (x,y)

接下来要对这些矩形进行 m m m次移动操作。每次移动会选择一个矩形,具体的移动可以用方向和距离表示。方向分别为,上、下、左、右、左上、左下、右上、右下,方向向量分别为 ( 1 , 0 ) , ( 1 , 1 ) , ( 0 , 1 ) , ( − 1 , 1 ) , ( − 1 , 0 ) , ( − 1 , − 1 ) , ( 0 , − 1 ) , ( 1 , − 1 ) (1,0),(1,1),(0,1),(-1,1),(-1,0),(-1,-1),(0,-1),(1,-1) (1,0),(1,1),(0,1),(1,1),(1,0),(1,1),(0,1),(1,1),距离为一个正整数 d d d

假设矩形的左下角的坐标为 ( a , b ) (a,b) (a,b),方向向量为 ( d x , d y ) (dx,dy) (dx,dy),那么矩形的左下角会移动到位置 ( a + d × d x , b + d × d y ) (a+d\times dx,b+d\times dy) (a+d×dx,b+d×dy),右上角也同理。同时,这个矩形在移动的过程中,会把中间过程都保留下来。也就是说,矩形移动到位置 ( a + d × d x , b + d × d y ) (a+d\times dx,b+d\times dy) (a+d×dx,b+d×dy),会产生 d d d个新的矩形,左下角为 ( a + i × d x , b + i × d y ) (a+i\times dx,b+i\times dy) (a+i×dx,b+i×dy),其中 0 ≤ i ≤ d − 1 0\leq i\leq d-1 0id1,大小和原矩形相同。

m m m次操作之后,有 q q q次询问,每次给你一个点 ( p x , p y ) (px,py) (px,py),问有多少个矩形包含这个点。

0 ≤ n , m , q ≤ 250000 0\leq n,m,q\leq 250000 0n,m,q250000,所有坐标范围在 1 1 1 250000 250000 250000之间。

这里的坐标范围指矩形在所有时间的坐标都在这个范围内,并且查询的点也在这个范围内。

时间限制 3000 m s 3000ms 3000ms,空间限制 512 M B 512MB 512MB


题解

考虑差分,对于一个矩形,我们可以将其差分成坐标系上的四个带权的点,那么矩形的移动就可以差分成若干条带权的边。于是,每次查询就是求差分后的图的二维前缀和。

对于方向相反的操作我们可以看作同一种,也就是说,线段只会有横着、竖着、左斜和右斜两种。

对于横着的线段,我们竖着跑扫描线,边修改边查询,注意同一位置的修改应在查询前面。

对于竖着的线段,我们可以将坐标轴翻转,然后就可以按处理横着的线段的方法来处理了。

对于左斜的线段,我们发现每条线段上的点的 x + y x+y x+y是相同的,于是我们可以右斜着跑扫描线。对于每次修改,我们在 x x x方向和 y y y方向上都做修改。对于每次查询,设查询的点为 ( x , y ) (x,y) (x,y),我们观察图像。

在这里插入图片描述
我们查询 x x x方向上 1 1 1 x x x的权值和,那么我们求出的部分是红色部分和蓝色部分。然后,我们减去 y y y方向上 y + 1 y+1 y+1 x + y x+y x+y的权值和,也就是蓝色部分,这样即可得到红色部分,也就是点 ( x , y ) (x,y) (x,y)的二维前缀和了。

对于右斜的线段,我们发现每条线段上的点的 y − x y-x yx是相同的,于是我们左斜着跑扫描线。对于每次修改,我们在 x x x方向和 y y y方向上都做修改。对于每次查询,设查询的点为 ( x , y ) (x,y) (x,y),我们观察图像。

在这里插入图片描述
我们先按 y − x y-x yx从小到大来操作,将修改存储在 x x x方向上,每次查询 x x x方向上 1 1 1 x x x的权值和,即可得到红色部分。然后,再按 y − x y-x yx从大到小来操作,将修改存储在 y y y方向上,每次查询 y y y方向双上 1 1 1 y y y的权值和,即可得到蓝色部分,两者相加就是点 ( x , y ) (x,y) (x,y)的二维前缀和了。

注意在第二次操作时,对于相同的位置,要先查询再修改。因为相同位置的修改在第一次操作时已经被计算过贡献了,第二次就不需要再算贡献了。

修改和查询可以用线段树维护,时间复杂度为 O ( ( n + m + q ) log ⁡ v ) O((n+m+q)\log v) O((n+m+q)logv),其中 v v v为坐标范围的大小。

注意

  • 因为没有移动的矩形也算贡献,所以可以在一开始将所有矩形都算一次贡献,每次移动就不用算最开始的矩形的贡献。算一开始每个矩形的贡献可以看作矩形从它左边一个单位的位置往右移一个位置到原来的位置,这样统计的就是矩形原本的位置
  • 虽然坐标范围是 1 1 1 250000 250000 250000,但差分时会有一些点超出坐标范围,然而不会超出太多,所以我们只需要再线段树上将范围开大一点点,比如 1 1 1 250005 250005 250005

code

#include<bits/stdc++.h>
#define lc k<<1
#define rc k<<1|1
using namespace std;
const int N=250005;
int n,m,q;
int dx[8]={1,1,0,-1,-1,-1,0,1},dy[8]={0,1,1,1,0,-1,-1,-1};
long long ans[N+5];
struct vt{
	int dx,dy,ux,uy;
}w[N+5];
struct line{
	int x,y,t,z,que;
};
struct tree{
	long long tr[4*N+5],ly[4*N+5];
	void build(int k,int l,int r){
		tr[k]=ly[k]=0;
		if(l==r) return;
		int mid=l+r>>1;
		build(lc,l,mid);
		build(rc,mid+1,r);
	}
	void down(int k,int l,int r){
		int mid=l+r>>1;
		tr[lc]+=ly[k]*(mid-l+1);
		tr[rc]+=ly[k]*(r-mid);
		ly[lc]+=ly[k];
		ly[rc]+=ly[k];
		ly[k]=0;
	}
	void ch(int k,int l,int r,int x,int y,long long z){
		if(l>=x&&r<=y){
			tr[k]+=(r-l+1)*z;ly[k]+=z;
			return;
		}
		if(ly[k]) down(k,l,r);
		int mid=l+r>>1;
		if(x<=mid) ch(lc,l,mid,x,y,z);
		if(y>mid) ch(rc,mid+1,r,x,y,z);
		tr[k]=tr[lc]+tr[rc];
	}
	int find(int k,int l,int r,int x,int y){
		if(l>=x&&r<=y) return tr[k];
		if(ly[k]) down(k,l,r);
		int mid=l+r>>1;
		long long re=0;
		if(x<=mid) re+=find(lc,l,mid,x,y);
		if(y>mid) re+=find(rc,mid+1,r,x,y);
		return re;
	}
}tx,ty;
vector<line>v[4];
bool cmp1(line ax,line bx){
	if(ax.x!=bx.x) return ax.x<bx.x;
	return ax.que<bx.que;
}
bool cmp2(line ax,line bx){
	if(ax.x+ax.y!=bx.x+bx.y) return ax.x+ax.y<bx.x+bx.y;
	return ax.que<bx.que;
}
bool cmp3(line ax,line bx){
	if(ax.y-ax.x!=bx.y-bx.x) return ax.y-ax.x<bx.y-bx.x;
	return ax.que<bx.que;
}
void add(int p,int x,int y,int t,int z){
	if(p==0) v[p].push_back((line){y,x+1,t-1,z,0});
	else if(p==1) v[p].push_back((line){x+1,y+1,t-1,z,0});
	else if(p==2) v[p].push_back((line){x,y+1,t-1,z,0});
	else if(p==3) v[p].push_back((line){x-1,y+1,t-1,z,0});
	else if(p==4) v[p-4].push_back((line){y,x-t,t-1,z,0});
	else if(p==5) v[p-4].push_back((line){x-t,y-t,t-1,z,0});
	else if(p==6) v[p-4].push_back((line){x,y-t,t-1,z,0});
	else v[p-4].push_back((line){x+t,y-t,t-1,z,0});
}
void pt(int p,int id,int t){
	vt &k=w[id];
	add(p,k.dx,k.dy,t,1);
	add(p,k.dx,k.uy+1,t,-1);
	add(p,k.ux+1,k.dy,t,-1);
	add(p,k.ux+1,k.uy+1,t,1);
	k.dx+=t*dx[p];k.ux+=t*dx[p];
	k.dy+=t*dy[p];k.uy+=t*dy[p];
}
void solve1(vector<line>v1){
	sort(v1.begin(),v1.end(),cmp1);
	tx.build(1,1,N);
	for(int i=0;i<v1.size();i++){
		if(!v1[i].que) tx.ch(1,1,N,v1[i].y,v1[i].y+v1[i].t,v1[i].z);
		else ans[v1[i].que]+=tx.find(1,1,N,1,v1[i].y);
	}
}
void solve2(vector<line>v1){
	sort(v1.begin(),v1.end(),cmp2);
	tx.build(1,1,N);
	ty.build(1,1,N);
	for(int i=0;i<v1.size();i++){
		if(!v1[i].que){
			tx.ch(1,1,N,v1[i].x-v1[i].t,v1[i].x,v1[i].z);
			ty.ch(1,1,N,v1[i].y,v1[i].y+v1[i].t,v1[i].z);
		}
		else{
			ans[v1[i].que]+=
				tx.find(1,1,N,1,v1[i].x)-ty.find(1,1,N,v1[i].y+1,v1[i].x+v1[i].y);
		}
	}
}
void solve3(vector<line>v1){
	sort(v1.begin(),v1.end(),cmp3);
	tx.build(1,1,N);
	for(int i=0;i<v1.size();i++){
		if(!v1[i].que) tx.ch(1,1,N,v1[i].x,v1[i].x+v1[i].t,v1[i].z);
		else ans[v1[i].que]+=tx.find(1,1,N,1,v1[i].x);
	}
	ty.build(1,1,N);
	for(int i=(int)v1.size()-1;i>=0;i--){
		if(!v1[i].que) ty.ch(1,1,N,v1[i].y,v1[i].y+v1[i].t,v1[i].z);
		else ans[v1[i].que]+=ty.find(1,1,N,1,v1[i].y);
	}
}
int main()
{
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=n;i++){
		scanf("%d%d%d%d",&w[i].dx,&w[i].dy,&w[i].ux,&w[i].uy);
		--w[i].dx;--w[i].ux;pt(0,i,1);
	}
	for(int i=1,p,id,t;i<=m;i++){
		scanf("%d%d%d",&p,&id,&t);
		pt(p,id,t);
	}
	for(int i=1,x,y;i<=q;i++){
		scanf("%d%d",&x,&y);
		v[0].push_back((line){y,x,0,0,i});
		v[1].push_back((line){x,y,0,0,i});
		v[2].push_back((line){x,y,0,0,i});
		v[3].push_back((line){x,y,0,0,i});
	}
	solve1(v[0]);solve1(v[2]);
	solve2(v[3]);solve3(v[1]);
	for(int i=1;i<=q;i++){
		printf("%lld\n",ans[i]);
	}
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1137117.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

app分发的一些流程2

应用分发的流程通常包括以下步骤&#xff1a; 开发应用程序&#xff1a;首先&#xff0c;您需要开发您的应用程序。这包括编写代码、设计用户界面、测试应用程序等等。确保您的应用程序符合各个应用商店的规范和要求&#xff0c;以确保顺利通过审核。 准备应用材料&#xff1…

Android 13.0 自定义仿小米全面屏手势导航左右手势滑动返回UI效果

1.概述 在13.0的系统产品开发中,对于设置默认系统手势的左右滑动返回UI,系统默认的是比较简单,产品需求要求仿小米华为的左右手势返回UI样式的定制,所以需要找到绘制手势返回UI的相关代码,然后自定义手势导航左右滑动返回的相关UI就可以了 接下来就来实现手势导航做好手势…

windows殺死端口

netstat -ano | findstr 8081 taskkill /F /PID taskkill /F /PID 16624

796. 子矩阵的和(左上角前缀和)

题目&#xff1a; 796. 子矩阵的和 - AcWing题库 思路&#xff1a; 1.暴力搜索&#xff08;搜索时间复杂度为O(n2)&#xff0c;很多时候会超时&#xff09; 2. 前缀和&#xff08;左上角前缀和&#xff09;&#xff1a;本题特殊在不是直接求前n个数的和&#xff0c;而是求…

竞赛 深度学习图像分类算法研究与实现 - 卷积神经网络图像分类

文章目录 0 前言1 常用的分类网络介绍1.1 CNN1.2 VGG1.3 GoogleNet 2 图像分类部分代码实现2.1 环境依赖2.2 需要导入的包2.3 参数设置(路径&#xff0c;图像尺寸&#xff0c;数据集分割比例)2.4 从preprocessedFolder读取图片并返回numpy格式(便于在神经网络中训练)2.5 数据预…

Leo赠书活动-03期 【ChatGPT 驱动软件开发:AI 在软件研发全流程中的革新与实践 】

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; 赠书活动专栏 ✨特色专栏&#xff1a;…

Java学习 习题 1.

一、 1.2. 3. 4. 5. 二、 1. 2. 3. 4. 5. 6. 7. 8.

【Java技术专题】「入门到精通系列教程」深入探索Java特性中并发编程体系的原理和实战开发指南( 实现可伸缩IO专题)— 上

深入探索Java特性中并发编程体系的原理和实战开发指南&#xff08; 实现可伸缩IO专题&#xff09; 总体内容概览可扩展的网络服务分布式对象传统的阻塞式网络服务每个请求或连接可以在独立的线程中进行处理Server服务处理请求类Handler处理逻辑类优点缺点 可扩展性目标平稳降级…

zotero word联动 如何使用Zotero在Word中插入参考文献

下载https://gitee.com/redleafnew00/Chinese-STD-GB-T-7714-related-csl里面论文要求的格式&#xff0c;或者word里面点这个再搜 输入好以后一定要输入空格&#xff01;

RK3399平台开发系列讲解(基础篇)嵌入式编码规范有哪些

🚀返回专栏总目录 文章目录 一、什么是GNU二、GNU C 编码规范2.1、格式2.2、注释2.3、语法约定2.4、命名2.5、系统可移植性2.6、CPU 可移植性2.7、系统函数2.8、国际化2.9、字符集沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 GNU 编码规范的出发点,是确保所有 G…

【C语言】strcpy()函数

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:C语言 ⚙️操作环境:Visual Studio 2022 目录 一.strcpy()函数简介 1.函数功能 2.函数参数 1>.char * destination 2>.const char * source 3.函数返回值 4.函数头文件 二.strcpy()函数的具体使用 1.使用s…

【数据结构练习】树和二叉树的选择题精选集锦

前言 编程想要学的好&#xff0c;刷题少不了&#xff0c;我们不仅要多刷题&#xff0c;还要刷好题&#xff01;为此我开启了一个弯道超车必做好题锦集的系列&#xff0c;此为树和二叉树的选择题精选集锦。该系列会不定期更新&#xff0c;敬请期待&#xff01; 1.已知某二叉树的…

c++学习之搜索二叉树

目录 一&#xff0c;什么是搜索二叉树&#xff1f; 二&#xff0c;搜索二叉树的实现 非递归实现 节点与类成员 插入 查找 删除 递归实现 插入 查找 删除 一&#xff0c;什么是搜索二叉树&#xff1f; 搜索二叉树&#xff08;Binary Search Tree&#xff09;是一种常见…

jdbc 执行批处理任务

package com.csdn.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; //演示执行批处理任务 public class ExecuteBatchTask {public static void main(String[] args) throws ClassNotFou…

通过jsonobject.tostring 传字符串为有空格问题

目录 通过jsonobject.tostring 传字符串为有空格问题 1.问题原因解决思路解决方案总结参考 文章所属专区 项目问题解决 1.问题原因 通过JSONObject.toString()方法将字符串转换为JSON格式时&#xff0c;可能会出现空格的情况。这是因为JSONObject.toString()方法在生成JSON字…

理解什么是接口测试?怎样做接口测试?

一 什么是接口&#xff1f; 接口测试主要用于外部系统与系统之间以及内部各个子系统之间的交互点&#xff0c;定义特定的交互点&#xff0c;然后通过这些交互点来&#xff0c;通过一些特殊的规则也就是协议&#xff0c;来进行数据之间的交互。接口测试主要用于外部系统与系统之…

贪心算法学习——单调递增的数字

一&#xff0c;单调递增的数字 1.题目 当且仅当每个相邻位数上的数字 x 和 y 满足 x < y 时&#xff0c;我们称这个整数是单调递增的。 给定一个整数 n &#xff0c;返回 小于或等于 n 的最大数字&#xff0c;且数字呈 单调递增 。 示例 1: 输入: n 10 输出: 9示例 2: 输入…

【c++速通】入门级攻略:引用详解 | auto的类型推导 | 不一样的for循环 | nullptr版本空指针

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; C入门到进阶 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言&#x1f324;️引用☁️引用的概念☁️引用的特性⭐引用在定义时必须初始化 ☁️常引用…

javascript IP地址正则表达式

注&#xff1a; 一定不要把表达式赋值给变量&#xff0c;直接表达式.test() /^(1[0-9]{2}|2[0-4][0-9]|25[0-5]|(\d){1,2})\.(1[0-9]{2}|2[0-4][0-9]|25[0-5]|(\d){1,2}|0)\.(1[0-9]{2}|2[0-4][0-9]|25[0-5]|(\d){1,2}|0)\.(1[0-9]{2}|2[0-4][0-9]|25[0-5]|(\d){1,2}|0)$/g.te…

【转载】双亲委派模型

双亲委派模型是 Java 类加载器的一种工作模式&#xff0c;通过这种工作模式&#xff0c;Java 虚拟机将类文件加载到内存中&#xff0c;这样就保证了 Java 程序能够正常的运行起来。那么双亲委派模型究竟说的是啥呢&#xff1f;接下来我们一起来看。 1.类加载器 双亲委派模型针…