luogu P1110 [ZJOI2007]报表统计

背景:

调了 3 3 3天,查了一下 s e t set set中的 l o w e r _ b o u n d lower\_bound lower_bound的时间复杂度,发现了…
然后跑到了 r a n k 10 rank10 rank10

题目传送门:

https://www.luogu.org/problemnew/show/P1110

题意:

刚开始有 n n n个数,共 n n n块。
三个操作:
1. 1. 1.在第 n n n个块的最后一个位置加上一个数;
2. 2. 2.查找相邻两个数的差值的最小值;
3. 3. 3.查找所有数中的差值的最小值。

思路:

发现没有修改操作。
那就考虑 S T L STL STL吧。

考虑操作 2 2 2先。
最小值的维护显然想到了堆。不妨开一个堆来记录相邻两个的差值。然后对于每一个块的贡献是单调不递增的,直接用一个数组维护。
在加入操作时,不妨给要删除的堆中的元素打上删除标记,当它为堆顶时,直接删除。然后更新数组和堆即可。

在考虑操作 3 3 3
直接用 s e t set set维护数,没有什么难度,贡献也是单调不递减的。
调了 3 3 3天发现: S T L STL STL中的 l o w e r _ b o u n d lower\_bound lower_bound s e t set set中使用时间复杂度是 Θ ( n l o g n ) \Theta(nlogn) Θ(nlogn)的,要用 s e t set set中的 l o w e r _ b o u n d lower\_bound lower_bound使得时间复杂度为 Θ ( l o g n ) \Theta(logn) Θ(logn)
坑死,但也学到了…

代码:

#include<cstdio>
#include<cstring>
#include<queue>
#include<set>
#include<vector>
#include<algorithm>
#define ID set<int>::iterator
using namespace std;
struct node
{
	int id;
	int x;
	bool friend operator <(const node x,const node y)
	{
		return x.x>y.x;
	}
};
priority_queue<node> f;
set<int> p;
	int n,m,ans=2147483647,p_min=2147483647;
	int front[500010],last[500010],will[500010];
	char s[20];
int read()
{
	int x=0,f=1;
	char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar())
		if(ch=='-') f=-1;
	for(;ch>='0'&&ch<='9';x=(x<<3)+(x<<1)+(ch^48),ch=getchar());
	return x*f;
}
void work1(int y)
{
	if(!p_min||*p.lower_bound(y)==y) {p_min=0;return;}
	p.insert(y);
	ID now=p.lower_bound(y),i,j;
	if(now!=p.begin())
	{
		i=--now;
		now++;
		p_min=min(p_min,abs(y-*i));
	}
	if(now!=p.end())
	{
		j=++now;
		if(j==p.end()) return;
		now--;
		p_min=min(p_min,abs(*j-y));
	}
}
void work2(int x,int y)
{
	last[x]=y;
	will[x]=abs(last[x]-front[x+1]);
	while(!f.empty()&&f.top().x!=will[f.top().id])
		f.pop();
	f.push((node){x,will[x]});
}
int main()
{
	int x,y=2147483647;
	n=read(),m=read();
	memset(front,63,sizeof(front));
	memset(last,63,sizeof(last));
	p.insert(-1000000000),p.insert(1000000000);
	for(int i=1;i<=n;i++)
	{
		x=read();
		if(p_min||*p.lower_bound(x)!=x) p.insert(x); else p_min=0;
		will[i-1]=abs(x-y);
		f.push((node){i-1,will[i-1]});
		front[i]=last[i]=x;
		y=x;
	}
	if(p_min)
		for(ID i=p.begin();i!=p.end();i++)
		{
			ID j=++i;
			if(j==p.end()) break;
			i--;
			p_min=min(p_min,abs(*i-*j));
		}
	for(int i=1;i<=m;i++)
	{
		scanf("%s",s+1);
		if(s[1]=='I')
		{
			x=read(),y=read();
			work1(y);
			ans=min(ans,abs(y-last[x]));
			if(y!=last[x]) work2(x,y);
		}
		else if(s[5]=='G')
		{
			while(!f.empty()&&f.top().x!=will[f.top().id])
				f.pop();
			printf("%d\n",min(ans,f.top().x));
		}
		else
		{
			printf("%d\n",p_min);
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值