当前位置: 首页 > news >正文

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

题意

小 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.mrgr.cn/news/92987.html

相关文章:

  • mapbox基础,使用点类型geojson加载symbol符号图层,用于标注文字
  • 回归算法模型总结
  • Bilibili 视频弹幕自动获取和自定义屏蔽词
  • 【牛客】第 k 小
  • vue3之echarts仪表盘
  • RPA 职业前景:个人职场发展的 “新机遇”
  • 动态SQL
  • 【经验分享】Ubuntu vmware虚拟机存储空间越来越小问题(已解决)
  • Unity 打包后EXE运行出现Field to Load il2cpp的一种情况
  • [KEIL]单片机技巧 01
  • 2025-03-03 学习记录--C/C++-PTA 7-38 数列求和-加强版
  • 【监督学习】支持向量机步骤及matlab实现
  • Excel的行高、列宽单位不统一?还是LaTeX靠谱
  • 从DNS到TCP:DNS解析流程和浏览器输入域名访问流程
  • Vue盲区扫雷
  • 大语言模型学习
  • 【弹性计算】弹性裸金属服务器和神龙虚拟化(一):功能特点
  • 大语言模型学习--LangChain
  • GPIO及其应用
  • 【弹性计算】弹性裸金属服务器和神龙虚拟化(二):适用场景