洛谷 P11830 省选联考2025 幸运数字 题解

embedded/2025/3/6 15:29:44/

题意

小 X 有 n n n 个正整数二元组 ( a i , b i ) ( 1 ≤ i ≤ n ) (a_i, b_i) (1 \leq i \leq n) (ai,bi)(1in)。他将会维护初始为空的可重集 S S S,并对其进行 n n n 轮操作。第 i ( 1 ≤ i ≤ n ) i (1 \leq i \leq n) i(1in) 轮操作中,他会在 S S S 中加入 a i a_i ai b i b_i bi

m = ∑ i = 1 n a i m = \sum \limits_{i=1}^{n} a_i m=i=1nai,在所有操作结束后,小 X 会得到一个包含 m m m 个正整数的可重集 S S S。最后他会计算 S S S 的中位数,即 S S S 中第 ⌊ m + 1 2 ⌋ \left\lfloor \frac{m+1}{2} \right\rfloor 2m+1 小的数,作为他的幸运数字。

想知道小 X 幸运数字的小 Y 不知道这 n n n 个二元组的具体数值是多少,但她得知了每个数的范围。具体地,对于每个 1 ≤ i ≤ n 1 \leq i \leq n 1in,小 Y 知道 a i ∈ [ l i , 1 , r i , 1 ] a_i \in [l_{i,1}, r_{i,1}] ai[li,1,ri,1] b i ∈ [ l i , 2 , r i , 2 ] b_i \in [l_{i,2}, r_{i,2}] bi[li,2,ri,2]

小 Y 想知道在满足以上条件的情况下,有多少个数可能成为小 X 的幸运数字。

∑ n \sum n n 为单个测试点内所有测试数据的 n n n 的和。对于所有测试点:

  • 1 ≤ T ≤ 400 1 \leq T \leq 400 1T400
  • 1 ≤ n ≤ 2 × 1 0 5 1 \leq n \leq 2 \times 10^5 1n2×105 1 ≤ ∑ n ≤ 6 × 1 0 5 1 \leq \sum n \leq 6 \times 10^5 1n6×105
  • ∀ 1 ≤ i ≤ n \forall 1 \leq i \leq n ∀1in 1 ≤ l i , 1 ≤ r i , 1 ≤ 1 0 9 1 \leq l_{i,1} \leq r_{i,1} \leq 10^9 1li,1ri,1109 1 ≤ l i , 2 ≤ r i , 2 ≤ 1 0 9 1 \leq l_{i,2} \leq r_{i,2} \leq 10^9 1li,2ri,2109

思路

假如有一个数列,一个值 m m m 的个数为 b b b,比它小的数的个数为 a a a,比它大的数的个数为 c c c,对于中间位置 m i d = ⌊ a + b + c + 1 2 ⌋ mid=\left \lfloor \frac{a+b+c+1}{2} \right \rfloor mid=2a+b+c+1

  • m i d ≤ a mid\le a mida,那么中间位置在小于 m m m 的数中,中位数小于 m m m
  • a < m i d ≤ a + b a<mid\le a+b a<mida+b,那么中间位置在一堆 m m m 中,中位数就是 m m m
  • m i d > a + b mid>a+b mid>a+b,那么中间位置在大于 m m m 的数中,中位数大于 m m m

那么,当我们知道 m m m 的个数、小于和大于 m m m 的数的个数,就可以知道 m m m 是否为中位数。将这样的操作集成为函数 M i d ( a , b , c ) \rm {Mid}(a,b,c) Mid(a,b,c),三种类型分别为 − 1 , 0 , 1 -1,0,1 1,0,1

但是题目中给的是个数的区间,那要怎么办呢?

考虑从题目给的“个数区间”下手,如果可以维护两对区间:小于 m m m 的数的个数区间为 [ l l e s , r l e s ] [l_{les},r_{les}] [lles,rles],大于 m m m 的数的个数区间为 [ l b i g , r b i g ] [l_{big},r_{big}] [lbig,rbig]。如果存在 Mid ( r l e s , c n t m , l b i g ) = 0 \textup{Mid}(r_{les},cnt_m,l_{big})=0 Mid(rles,cntm,lbig)=0 或者 Mid ( l l e s , c n t m , r b i g ) \textup{Mid}(l_{les},cnt_m,r_{big}) Mid(lles,cntm,rbig),显然 m m m 为中位数。

还有两种情况就是,小于 m m m 的数的个数取得少、大于 m m m 的数的个数取得多,或者小于 m m m 的数的个数取得多、大于 m m m 的数的个数取得少。这两种情况的可行性体现于:
Mid ( r l e s , c n t m , l b i g ) ≠ Mid ( l l e s , c n t m , r b i g ) \textup{Mid}(r_{les},cnt_m,l_{big})\neq\textup{Mid}(l_{les},cnt_m,r_{big}) Mid(rles,cntm,lbig)=Mid(lles,cntm,rbig)

(感性理解就是)因为两端个数 a ∈ [ l l e s , r l e s ] a\in [l_{les},r_{les}] a[lles,rles] c ∈ [ l b i g , r b i g ] c\in[l_{big},r_{big}] c[lbig,rbig] a a a c c c 可以取各自区间的任意一个数;而“不等于”相当于存在 r l e s > l b i g , l l e s < r b i g r_{les}>l_{big},l_{les}<r_{big} rles>lbig,lles<rbig 或者 r l e s < l b i g , l l e s > r b i g r_{les}<l_{big},l_{les}>r_{big} rles<lbig,lles>rbig,可以构造出 m m m 是中位数的情况。

ll Mid(ll a,ll b,ll c)
{ll mid=(a+b+c+1)>>1;if(a>=mid)return -1;if(a+b>=mid)return 0;return 1;
}
bool MID(ll m)
{ll c1=Mid(les.r,m,big.l),c2=Mid(les.l,m,big.r);return m&&(!c1||!c2||c1!=c2);
}

那只要能维护出上文的两对区间就可以了。

因为只求个数,不需要知道具体哪一个,而且注意到题目给的 l i , 1 , r i , 1 , l i , 2 , r i , 2 l_{i,1},r_{i,1},l_{i,2},r_{i,2} li,1,ri,1,li,2,ri,2 较大,考虑离散化。然后套路地用两个动态数组分别季度左右端点的信息,以便进行枚举过程中的信息修改。

枚举一堆 m m m 是否为中位数时,贪心地取 m m m 的最多数量(比较明显的贪心)。

先初始化 l e s les les b i g big big 区间,然后直接在离散化的下标哪里扫过去:在 m m m m + 1 m+1 m+1 的转移中,使用先前维护的动态数组,分别加入和删除当前中位数和不符合条件的中位数的信息:从 b i g big big 区间删去当前中位数的信息,在 l e s les les 区间中加入被弹出的数的信息。

答案就是相邻区间个数和。使用离散后的数组就可以计算。

一些细节和写法见代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=1e6+9,inf=0x7f7f7f7f;
int id,T,n;
struct term
{ll l,r;
}a[N],b[N],les,big;
ll bb[N];
ll Mid(ll a,ll b,ll c)
{ll mid=(a+b+c+1)>>1;if(a>=mid)return -1;if(a+b>=mid)return 0;return 1;
}
bool MID(ll m)
{ll c1=Mid(les.r,m,big.l),c2=Mid(les.l,m,big.r);return m&&(!c1||!c2||c1!=c2);
}
vector<term>s[N],t[N];
void clean(ll nn)
{for(int i=1;i<=nn;i++)s[i].clear(),t[i].clear();
}
int main()
{scanf("%d%d",&id,&T);while(T--){scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%lld%lld%lld%lld",&a[i].l,&a[i].r,&b[i].l,&b[i].r);bb[i*2-1]=b[i].l;bb[i*2]=b[i].r+1;}sort(bb+1,bb+2*n+1);ll nn=unique(bb+1,bb+2*n+1)-bb-1;les.l=les.r=big.l=big.r=0;//在中位数枚举的转移中,动态维护两对区间//比中位数大/小的个数区间 //[les.l,les.r] cntmid [big.l,big.r]for(int i=1;i<=n;i++){b[i].l=lower_bound(bb+1,bb+nn+1,b[i].l)-bb;//离散化b[i].r=lower_bound(bb+1,bb+nn+1,b[i].r+1)-bb;//	cout<<b[i].l<<" "<<b[i].r<<endl;s[b[i].l].push_back(a[i]);//套路地,只记录左右端点信息t[b[i].r].push_back(a[i]);big.l+=a[i].l,big.r+=a[i].r;//big区间初始化}ll cntmid=0,ret=0;for(int i=1;i<=nn;i++){for(auto x:s[i]){cntmid+=x.r;//加入中位数,贪心的取数量更多的中位数 big.l-=x.l,big.r-=x.r;//从big区间删去}for(auto x:t[i]){cntmid-=x.r;//弹出不符合条件的 les.l+=x.l,les.r+=x.r;//弹出的加入les区间 }//	cout<<les.l<<" "<<les.r<<" "<<big.l<<" "<<big.r<<endl;if(MID(cntmid))ret+=bb[i+1]-bb[i];//这段区间都能是中位数,统计个数}printf("%lld\n",ret);clean(nn);}return 0;
}

http://www.ppmy.cn/embedded/170508.html

相关文章

SPI硬件设计及通信原理解析

SPI(Serial Peripheral interface,串行外围设备接口),是一种高速的,全双工,同步通信总线。 SPI采用主从控制模式(Master--Slave)架构,一般有1个主设备、一个或多个从设备,使得主设备可以与多个从设备之间实现片间通信。 SPI在芯片管脚中只占用四根线节约了芯片的管脚…

记一次误禁用USB导致键盘鼠标失灵的修复过程

背景说明 在电脑上插入了一个USB hub&#xff0c;然后弹窗提示&#xff1a;“集线器端口上出现电涌”&#xff0c;点开让选择“重置”或者“关闭”&#xff0c;不小心点了关闭&#xff0c;结果这个usb口就被关了&#xff0c;再插任何东西都没反应&#xff0c;找了很多办法都恢…

前端基础之消息订阅与发布

需要下载npm I pubsub-js 在Student.vue中发送数据 <template> <div class"demo"> <h2 class"title">学生姓名:{{name}}</h2> <h2>学生性别:{{sex}}</h2> <button click"sendStudentName">将学生名给…

Android中的Loader机制

Android中的Loader机制是在Android 3.0版本之后引入的一种异步加载数据的方法。以下是对Android中Loader机制的详细解释&#xff1a; 一、Loader机制的基本概念 Loader是Android提供的一种异步加载数据的机制&#xff0c;它可以在后台线程中执行耗时操作&#xff0c;并在加载…

Java面试知识点总结

目录 基础篇 一、JAVA: 二、JDK ?JRE ?JVM的区别&#xff1a; 三、java的基本数据类型 四、switch默认接受的几种数据类型 五、修饰符的作用 六、Static修饰符 七、final,finally,finalize 三者区别 八、StringBuffer StringBuilder String 区别 九、String的常用方…

前端埋点项目从设计到实现详解

了解前端埋点 前端埋点详解 “前端埋点项目从设计到实现详解”的问题&#xff0c;并且要参考提供的20条证据。首先&#xff0c;我得先理解前端埋点的基本概念和它在项目中的作用。根据证据&#xff0c;前端埋点主要是通过在代码中嵌入脚本来收集用户行为数据&#xff0c;比如点…

颠覆NLP的魔法:深度解读Transformer架构及其核心组件

目录 颠覆NLP的魔法&#xff1a;深度解读Transformer架构及其核心组件 一、Transformer 架构概述 二、核心组件解析 1. Self-Attention&#xff08;自注意力机制&#xff09; 2. 位置编码&#xff08;Positional Encoding&#xff09; 3. 多头注意力&#xff08;Multi-Hea…

CSS的列表属性

列表相关属性,可以用在ul,ol,li元素上. CSS属性名功能属性值 list-style-type 设置列表符号 常用值如下: none:不显示前面的标识(很常用!) square:实心方块 disc:圆形 decimal:数字 lower-roma:小写罗马字 upper-roman:大写罗马字 lower-alph:小写字母 upper-alpha:大写字母 …