NOIP2023模拟7联测28 花之舞

2023-11-02 03:12

本文主要是介绍NOIP2023模拟7联测28 花之舞,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目大意

有一个花园,每朵花可以表示为平面直角坐标系上的 N N N个点,第 i i i个点的坐标为 O ( x i , y i ) O(x_i,y_i) O(xi,yi)。定义两朵花之间的距离为它们的切比雪夫距离,即 d i s ( u , v ) = max ⁡ ( ∣ x u − x v ∣ , ∣ y u − y v ∣ ) dis(u,v)=\max(|x_u-x_v|,|y_u-y_v|) dis(u,v)=max(xuxv,yuyv)

q q q个询问,第 i i i个询问为一个区间 [ l i , r i ] [l_i,r_i] [li,ri],请确定一个 k ( l ≤ k ≤ r ) k(l\leq k\leq r) k(lkr),最大化 min ⁡ 1 ≤ u < v ≤ r , u ≠ k , v ≠ k d i s ( u , v ) \min\limits_{1\leq u<v\leq r,u\neq k,v\neq k}dis(u,v) 1u<vr,u=k,v=kmindis(u,v)。即,最大化:删掉一朵花后,区间里花之间距离最小值。

3 ≤ n ≤ 3 × 1 0 4 , 1 ≤ q ≤ 3 × 1 0 5 , 1 ≤ x i , y i ≤ 1 0 8 3\leq n\leq 3\times 10^4,1\leq q\leq 3\times 10^5,1\leq x_i,y_i\leq 10^8 3n3×104,1q3×105,1xi,yi108

1 ≤ l i < r i ≤ n , r i − l i ≥ 2 1\leq l_i<r_i\leq n,r_i-l_i\geq 2 1li<rin,rili2

保证不存在重合的点。

时间限制 4000 m s 4000ms 4000ms,空间限制 1024 M B 1024MB 1024MB


题解

显然,要最大化任意两点的最小值,最近点对的两个点必须删一个。那么,我们可以想办法求出所有在询问中可能用到的最近点对。

枚举 k k k 0 ≤ k ≤ log ⁡ v 0\leq k\leq \log v 0klogv,其中 v v v为值域),将平面直角坐标系分为大小为 2 k × 2 k 2^k\times 2^k 2k×2k的矩阵。对于每个 k k k,我们考虑求所有距离在 [ 2 k − 1 , 2 k ) [2^{k-1},2^k) [2k1,2k)上的点对(当然,求这个的目的是求出所有在询问中可能用到的最近点对,得到距离小于 2 k − 1 2^{k-1} 2k1的点对也无妨)。对于每个矩阵,找到相邻矩阵(相邻指共边或共顶点,也就是八连通)和自己的矩阵,对这九个矩阵分别进行操作。每次操作就是将这两个矩阵(自己和操作对象)的点按编号从小到大排序。这自己的矩阵为 A A A,操作对象的矩阵为 B B B,则对于 A A A中的每个点 x x x,在 B B B中找到最小的点 y y y y ≥ x y\geq x yx(注意 x , y x,y x,y是编号),将 z ∈ [ max ⁡ ( 0 , y − 8 ) , y − 1 ] z\in [\max(0,y-8),y-1] z[max(0,y8),y1]的点都存储在 x x x v e c t o r vector vector中,表示 x x x z z z可能在询问中构成最近点对。

为什么要取 z ∈ [ max ⁡ ( 0 , y − 8 ) , y − 1 ] z\in[\max(0,y-8),y-1] z[max(0,y8),y1]呢?因为如果 B B B中要取到 z ′ z' z,使得 z ′ < max ⁡ ( 0 , y − 8 ) z'<\max(0,y-8) z<max(0,y8) z ′ z' z x x x可能构成最近点对,则能让其构成最近点对的区间可定是包含 [ z ′ , x ] [z',x] [z,x]的。既然当前枚举的是距离在 [ 2 k − 1 , 2 k ) [2^{k-1},2^k) [2k1,2k)上的点对,则如果 B B B中存在 z ∈ [ max ⁡ ( 0 , y − 8 ) , y − 1 ] z\in [\max(0,y-8),y-1] z[max(0,y8),y1] z ′ z' z的距离小于 2 k − 1 2^{k-1} 2k1,则 z z z z ′ z' z的距离一定比 x x x z ′ z' z的距离小,也就是说只要询问包含 x x x z ′ z' z,则最近点对一定是 z z z z ′ z' z,也就是说 x x x z ′ z' z不可能在询问中构成最近点对。

我们发现,在最坏情况下,在 B B B中取 6 6 6个点之后,不管怎么加点,都会使得存在两个点的距离小于 2 k − 1 2^{k-1} 2k1。考虑到后面要删一个点,所以就取到 7 7 7 8 8 8为宜,所以上面取的区间为 [ max ⁡ ( 0 , y − 8 ) , y − 1 ] [\max(0,y-8),y-1] [max(0,y8),y1]。最后还要对每个点的 v e c t o r vector vector去重。

然后我们考虑询问怎么做。我们可以依照询问右边界升序扫描线,将可能的最近点对插入到线段树中。下面称上面处理出来的可能在询问中用到的最近点对为支配点对。

线段树的每个节点只考虑 左端点在节点对应的区间里 且 右端点不超过当前扫描到的边界 的支配点对,维护几个值,分别是:

  • 这些支配点对里,最近点对是哪对,距离是多少
  • 分别删掉上一条中最近点对中的一个点后的最近点对距离

合并区间的时候,设两边最近点对分别是 ( u 1 , v 1 ) (u_1,v_1) (u1,v1) ( u 2 , v 2 ) (u_2,v_2) (u2,v2),其中 u 1 < u 2 u_1<u_2 u1<u2。如果两边最近点对有共同端点,就可以将 删掉这个共同端点后的最近点对距离 更新为两边最小值;如果两边最近点对没有共同端点,那么一边的最近点对可能更新另一边的删掉某点后的最近点对距离。

询问时考虑删掉最近点对的哪个点,取最优即可。

时间复杂度为 O ( n log ⁡ n log ⁡ v + q log ⁡ n ) O(n\log n\log v+q\log n) O(nlognlogv+qlogn),其中 v v v x i , y i x_i,y_i xi,yi的值域。常数比较大,但时限有 5000 m s 5000ms 5000ms,是可以过的。

code

#include<bits/stdc++.h>
#define lc k<<1
#define rc k<<1|1
using namespace std;
const int N=30000,Q=300000,inf=2100000000;
int n,q,x[N+5],y[N+5],ans[Q+5];
vector<int>w[N+5];
vector<pair<int,int>>g[N+5];
map<pair<int,int>,vector<int>>mp;
struct node{int mn,x,y,v1,v2;
}tr[4*N+5];
int dis(int i,int j){return max(abs(x[i]-x[j]),abs(y[i]-y[j]));
}
void build(int k,int l,int r){tr[k]=(node){inf,-1,-1,inf,inf};if(l==r) return;int mid=l+r>>1;build(lc,l,mid);build(rc,mid+1,r);
}
node merge(node a,node b){node re;int v1,v2;if(a.mn<b.mn){v1=min(a.v1,b.mn);v2=a.v2;if(a.y==b.y) v2=min(v2,b.v2);else if(a.y==b.x) v2=min(v2,b.v1);else v2=min(v2,b.mn);re=(node){a.mn,a.x,a.y,v1,v2};}else{v1=b.v1;v2=b.v2;if(b.x==a.y) v1=min(v1,a.v2);else v1=min(v1,a.mn);if(b.y==a.y) v2=min(v2,a.v2);else v2=min(v2,a.mn);re=(node){b.mn,b.x,b.y,v1,v2};}return re;
}
void ch(int k,int l,int r,int x,int y){if(l==r&&l==x){int d=dis(x,y);if(d<tr[k].mn) tr[k]=(node){d,x,y,inf,tr[k].mn};else if(d<tr[k].v2) tr[k].v2=d;return;}int mid=l+r>>1;if(x<=mid) ch(lc,l,mid,x,y);else ch(rc,mid+1,r,x,y);tr[k]=merge(tr[lc],tr[rc]);
}
node find(int k,int l,int r,int x,int y){if(l>=x&&r<=y) return tr[k];int mid=l+r>>1;if(y<=mid) return find(lc,l,mid,x,y);if(x>mid) return find(rc,mid+1,r,x,y);return merge(find(lc,l,mid,x,y),find(rc,mid+1,r,x,y));
}
int main()
{freopen("flower.in","r",stdin);freopen("flower.out","w",stdout);scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%d%d",&x[i],&y[i]);for(int dv=0;dv<=27;dv++){mp.clear();for(int i=1;i<=n;i++){mp[{x[i]>>dv,y[i]>>dv}].push_back(i);}for(auto pv:mp){const auto &p=pv.first;const auto &v=pv.second;for(int dx=-1;dx<=1;dx++){for(int dy=-1;dy<=1;dy++){auto it=mp.find({p.first+dx,p.second+dy});if(it==mp.end()) continue;const auto &v1=it->second;for(int i=0,j=0;i<v.size();i++){while(j<v1.size()&&v1[j]<v[i]) ++j;for(int k=max(0,j-8);k<j;k++){w[v[i]].push_back(v1[k]);}}}}}}for(int i=1;i<=n;i++){sort(w[i].begin(),w[i].end());w[i].erase(unique(w[i].begin(),w[i].end()),w[i].end());}scanf("%d",&q);for(int i=1,l,r;i<=q;i++){scanf("%d%d",&l,&r);g[r].push_back({l,i});}build(1,1,n);for(int i=1;i<=n;i++){for(auto l:w[i]) ch(1,1,n,l,i);for(auto k:g[i]){int l=k.first,id=k.second;node re=find(1,1,n,l,i);ans[id]=max(re.v1,re.v2);}}for(int i=1;i<=q;i++) printf("%d\n",ans[i]);return 0;
}

这篇关于NOIP2023模拟7联测28 花之舞的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/327700

相关文章

CSS模拟 html 的 title 属性(鼠标悬浮显示提示文字效果)

《CSS模拟html的title属性(鼠标悬浮显示提示文字效果)》:本文主要介绍了如何使用CSS模拟HTML的title属性,通过鼠标悬浮显示提示文字效果,通过设置`.tipBox`和`.tipBox.tipContent`的样式,实现了提示内容的隐藏和显示,详细内容请阅读本文,希望能对你有所帮助... 效

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

usaco 1.2 Transformations(模拟)

我的做法就是一个一个情况枚举出来 注意计算公式: ( 变换后的矩阵记为C) 顺时针旋转90°:C[i] [j]=A[n-j-1] [i] (旋转180°和270° 可以多转几个九十度来推) 对称:C[i] [n-j-1]=A[i] [j] 代码有点长 。。。 /*ID: who jayLANG: C++TASK: transform*/#include<

hdu4431麻将模拟

给13张牌。问增加哪些牌可以胡牌。 胡牌有以下几种情况: 1、一个对子 + 4组 3个相同的牌或者顺子。 2、7个不同的对子。 3、13幺 贪心的思想: 对于某张牌>=3个,先减去3个相同,再组合顺子。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOExcepti

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟)

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟) 题目描述 给定一个链表,链表中的每个节点代表一个整数。链表中的整数由 0 分隔开,表示不同的区间。链表的开始和结束节点的值都为 0。任务是将每两个相邻的 0 之间的所有节点合并成一个节点,新节点的值为原区间内所有节点值的和。合并后,需要移除所有的 0,并返回修改后的链表头节点。 思路分析 初始化:创建一个虚拟头节点

每日一题|牛客竞赛|四舍五入|字符串+贪心+模拟

每日一题|四舍五入 四舍五入 心有猛虎,细嗅蔷薇。你好朋友,这里是锅巴的C\C++学习笔记,常言道,不积跬步无以至千里,希望有朝一日我们积累的滴水可以击穿顽石。 四舍五入 题目: 牛牛发明了一种新的四舍五入应用于整数,对个位四舍五入,规则如下 12345->12350 12399->12400 输入描述: 输入一个整数n(0<=n<=109 ) 输出描述: 输出一个整数

【算法专场】模拟(下)

目录 前言 38. 外观数列 算法分析 算法思路 算法代码 1419. 数青蛙 算法分析 算法思路 算法代码  2671. 频率跟踪器 算法分析 算法思路 算法代码 前言 在前面我们已经讲解了什么是模拟算法,这篇主要是讲解在leetcode上遇到的一些模拟题目~ 38. 外观数列 算法分析 这道题其实就是要将连续且相同的字符替换成字符重复的次数+

模拟实现vector中的常见接口

insert void insert(iterator pos, const T& x){if (_finish == _endofstorage){int n = pos - _start;size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;reserve(newcapacity);pos = _start + n;//防止迭代

PHP实现二叉树遍历(非递归方式,栈模拟实现)

二叉树定义是这样的:一棵非空的二叉树由根结点及左、右子树这三个基本部分组成,根据节点的访问位置不同有三种遍历方式: ① NLR:前序遍历(PreorderTraversal亦称(先序遍历)) ——访问结点的操作发生在遍历其左右子树之前。 ② LNR:中序遍历(InorderTraversal) ——访问结点的操作发生在遍历其左右子树之中(间)。 ③ LRN:后序遍历(PostorderT

【vue3|第28期】 Vue3 + Vue Router:探索路由重定向的使用与作用

日期:2024年9月8日 作者:Commas 签名:(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释:如果您觉在这里插入代码片得有所帮助,帮忙点个赞,也可以关注我,我们一起成长;如果有不对的地方,还望各位大佬不吝赐教,谢谢^ - ^ 1.01365 = 37.7834;0.99365 = 0.0255 1.02365 = 1377.4083;0.98365 = 0.0006 说