首页 > 其他分享 >P1110 [ZJOI2007] 报表统计 题解

P1110 [ZJOI2007] 报表统计 题解

时间:2024-02-28 15:33:57浏览次数:23  
标签:P1110 insert 10 题解 top ans s2 ZJOI2007 s1

考察点:STL 的熟练运用。

记:\(l_i\) 表示序列中不超过 \(a_i\) 的最大数,\(r_i\) 表示序列中超过 \(a_i\) 的最小数。

开一个 vector insert[N] 存储 \(a_i\) 后面插入的所有数字。

首先,我们使用一个 multiset s1 来存储相邻元素的差的绝对值,然后再开一个 multiset s2 来存储当前出现的所有元素,然后维护一个变量 \(ans\),表示操作三的答案。

此时,我们就可以直接完成操作二三了(操作二访问 *s1.begin(),操作三直接输出 \(ans\))。

下面我们来讲一下这些东西的预处理。

首先,我们将所有的 \(a_i\) 扔进 s2 中。根据 \(l_i\) 和 \(r_i\) 的定义,可以直接使用 lower/upper_bound() 函数找出,然后更新 \(ans=\min(ans,\min(|r_i-a_i|,|l_i-a_i))\),并将 \(|a_i-a_{i-1}|\) 插入到 s1 当中。

注意:我们在更新 \(ans\) 时,由于我们已经提前将所有 \(a_i\) 放入 s2 中,所以查找到的结果是 \(a_i\) 本身,此时需要将指针再前/后移一位,并判断是否越界。

预处理的代码如下:

inline void init(){
    for(int i=1;i<=n;++i){
		a[i]=read();
		if(i!=1)s1.insert(abs(a[i]-a[i-1]));
		s2.insert(a[i]);
	}
	for(int i=1;i<=n;++i){
		auto x=s2.lower_bound(a[i]);
		if(x!=s2.end())ans=min(ans,abs(a[i]-*(++x)));
		auto y=s2.upper_bound(a[i]);
		if(y!=s2.begin()&&--y!=s2.begin())ans=min(ans,abs(a[i]-*(--y)));
	}
}

接下来,我们只需要考虑操作一会带来的影响。

我们记 \(pre\) 表示当前位置的前一个元素。显然,如果 insert[x] 为空,那么 \(pre=a_x\),否则为 insert[x].back()(此时还没有插入 \(y\))。

我们考虑当前位置的加入会影响到哪些位置:类似链表插入,显然这次插入会断掉 \(pre\) 和 \(a_{x+1}\) 之间的联系(如果 \(x=n\),则忽略,否则 erase 时会 \(\color{purple}\text{RE}\)),并且使 \(pre\) 和 \(y\)、以及 \(y\) 和 \(a_{x+1}\) 成为邻居(同样 \(x=n\) 时不考虑)。然后使用 erase/insert 函数即可,注意删除单一元素时要用 find()

这样 s1 就处理完了。对于 s2,我们还是先 lower/upper_bound() 一下,找到最接近元素(先查找再插入,不用移动),更新答案然后将 \(y\) 同时扔到 s2insert[x] 里即可。

复杂度应该是单 \(\log\) 的,但是常数大,所以开了 O2 才能过(不开 \(70\))。话说有没有人帮我卡卡常啊

最后是完整代码(操作一使用函数封装了下,比较整洁,但是调用函数会变慢):

#include<bits/stdc++.h>
//#include<bits/extc++.h>
//#pragma GCC optimize("Ofast")
#define gt getchar
#define pt putchar
#define y1 y233
typedef long long ll;
//typedef __int128 lll;
typedef unsigned long long ull;
const int N=5e5+5;
using namespace std;
//using namespace __gnu_cxx;
//using namespace __gnu_pbds;
inline bool __(char ch){return ch>=48&&ch<=57;}
inline int read(){
   	int x=0;bool sgn=0;char ch=gt();
   	while(!__(ch)&&ch!=EOF){sgn|=(ch=='-');ch=gt();}
   	while(__(ch)){x=(x<<1)+(x<<3)+(ch-48);ch=gt();}
	return sgn?-x:x;
}
template<class T>
inline void print(T x){
	static char st[70];short top=0;
	if(x<0)pt('-');
    do{st[++top]=x>=0?(x%10+48):(-(x%10)+48),x/=10;}while(x);
    while(top)pt(st[top--]);
}
template<class T>
inline void printsp(T x){
	static char st[70];short top=0;
	if(x<0)pt('-');
    do{st[++top]=x>=0?(x%10+48):(-(x%10)+48),x/=10;}while(x);
    while(top)pt(st[top--]);pt(32);
}
template<class T>
inline void println(T x){
	static char st[70];short top=0;
	if(x<0)pt('-');
    do{st[++top]=x>=0?(x%10+48):(-(x%10)+48),x/=10;}while(x);
    while(top)pt(st[top--]);pt(10);
}
inline void put_str(string s){
	int siz=s.size();
	for(int i=0;i<siz-1;++i) pt(s[i]);
	printf("\n");
}
int n,m,a[N],ans=0x3f3f3f3f;
vector<int>insert[N];
multiset<int>s1,s2;
string opt;
inline void solve(int x,int y){
	x=read(),y=read(); 
	int pre=insert[x].size()?insert[x].back():a[x];
	insert[x].emplace_back(y);
	if(x!=n)s1.erase(s1.find(abs(pre-a[x+1])));
	s1.insert(abs(y-pre));
	if(x!=n)s1.insert(abs(y-a[x+1]));
	auto it=s2.lower_bound(y);
	if(it!=s2.end())ans=min(ans,abs(y-*it));
	auto it2=s2.upper_bound(y);
	if(it2!=s2.begin())ans=min(ans,abs(y-*(--it2)));
	s2.insert(y);
}
signed main(){
	n=read(),m=read();
	for(int i=1;i<=n;++i){
		a[i]=read();
		if(i!=1)s1.insert(abs(a[i]-a[i-1]));
		s2.insert(a[i]);
	}
	for(int i=1;i<=n;++i){
		auto x=s2.lower_bound(a[i]);
		if(x!=s2.end())ans=min(ans,abs(a[i]-*(++x)));
		auto y=s2.upper_bound(a[i]);
		if(y!=s2.begin()&&--y!=s2.begin())ans=min(ans,abs(a[i]-*(--y)));
	}
	for(int x,y;m;--m){
		cin>>opt;
		if(opt=="INSERT")solve(x,y);
		else if(opt=="MIN_GAP")println(*s1.begin());
		else println(ans);
	}
	return 0;
}

标签:P1110,insert,10,题解,top,ans,s2,ZJOI2007,s1
From: https://www.cnblogs.com/syzqwq/p/18040590

相关文章

  • CodeChef Chef and Churus 题解
    对给出的\(n\)个区间分块,设块长为\(B\)。每个块内计算每个位置的贡献(被覆盖次数)。具体地,记\(f_{i,j}\)表示第\(i\)个块第\(j\)个数被覆盖了多少次,这个可以用差分轻松维护。预处理复杂度\(O(\frac{n^2}{B})\)。现在考虑修改。记\(ans_i\)表示块\(i\)的贡献,那么对于......
  • ABC303 G 题解
    区间DP。设\(f_{l,r}\)表示只考虑\([l,r]\),先手得分减后手得分的最大值(并不关心谁是先手谁是后手),区间长度\(len=r-l+1\)。然后对三种情况分别讨论:使用操作一,此时取掉左/右端点的部分先手后手互换,对答案的贡献为\(\max(a_l-f_{l+1,r},a_r-f_{l,r-1})\)。使用操作二,继......
  • P1668 题解
    两种做法。一、最短路题目要求区间数量最小。如果能建出图来,就可以转换为最短路问题。具体地,我们从\(l-1\tor\)连一条长度为\(1\)的边,意味着要多经过\((l-1,r]\)这一个区间。这是左开右闭的形式。现在还有一个问题:通过这种边我们只能到达区间的右端点,如果想向左到达区......
  • P1266 速度限制 题解
    考虑分层图。把图按速度分成\(V\)层,\(f_{i,j}\)表示点\(i\)在第\(j\)层(速度为\(j\))的编号。注意:这里的速度为\(j\)是指到达\(i\)那一条边的速度(\(i\)为终点)。考虑一种naive的建边方式:首先,记当前点为\(u\),速度为\(i\);\(u\)的出边速度为\(j\),长度为\(l\),终点......
  • ABC302 Ex 题解
    首先我们考虑\(v\)固定怎么做。实际上就是ARC111B。考虑建图,对每个\((a_i,b_i)\)建一条无向边,那么问题就变成了:对于每条边都要选择它以及其连接的一个点,最大化选出的点数。很明显可以对每个连通块分开考虑。记当前连通块的点数为\(V\),边数为\(E\)。那么有结论:该连通块对......
  • ABC301 Ex 题解
    题意:你有一张无向连通图,定义\(d(s,t)\)表示从\(s\)到\(t\)所有路径中最大边权的最小值。现在给你三个数\(A_i,S_i,T_i\),让你求出当\(A_i\)这条边的边权\(+1\)时,\(d(S_i,T_i)\)会增加多少。首先考虑一下\(A_j+1\)什么时候会对\(d(S_j,T_j)\)有影响。\(A_j>d(S_......
  • 第五届图灵杯中级组题解
    网址赛时得分\(25+50+5+1=81\),rk67,逊死了。赛后发现T1爆搜+剪枝有\(50\)分,我【】【】。总结:还是菜。A.等差数列首先特判\(n\le4\)的情况。此时答案必然为Yes,只需要两两分组即可。由于第一个数必然在其中一个等差数列内,不妨强制令其在第一个等差数列内。考虑......
  • CF351D - Jeff and Removing Periods 题解
    首先做一点显然的转化:在进行第一次操作之后,可以将相同的数排在一起,这样一次就能删掉一种数。如果一开始就能删光一种数的话,那么次数就是区间颜色数,否则就是区间颜色数\(+1\)。所以现在原问题变成了两个问题:求区间内不同颜色数,判断区间内是否有某种颜色满足其出现位置构成等差数......
  • P9755 [CSP-S 2023] 种树 题解
    首先考虑如何求出第\(i\)棵树在\([l,r]\)时间段能长多高。这个东西可以差分一下然后等差数列求和。放一下代码:inlinelllcalc(inti,intx){ if(c[i]>=0){ return(lll)b[i]*x+(lll)c[i]*x*(x+1)/2; }else{ intd=(b[i]-1)/(-c[i]); if(x<=d)return(lll)b[i]*x+(l......
  • A-E题解
    A:对于任意一个满足条件的2*2矩阵,要么3个R和1个W,要么3个W和1个R。我们以3个R和1个W举例,只有以下4中情况满足:RR   RR   RW   WRRW  WR  RR   RR所以一种构造方法如下:奇数行全部放R;偶数行奇数列放R,偶数列放W即可。voidsolve(){intn;......