洛谷P1110 [ZJOI2007]报表统计 【无旋treap+堆】

题目链接

题意:

给出一个长度为 N N N 的原序列,有如下几种操作

  1. 在第 i 的位置后插入一个新元素 val ,若 i后面已经插入过元素,则插入元素的尾部(即若原序列为 1 2 3,在 2 后面插入 4 变为 1 2 4 3 ,再在2后面插入 5 则变为 1 2 4 5 3
  2. 查询所有相邻元素的差值绝对值的最小值 min_gap
  3. 查询所有元素差值绝对值的最小值 min_sort_gap

题解:

如果只有操作 1 和操作 3 ,就是平衡树的模板题,每插入一个数 val 都查询 val 前驱和后继与 val 的差值,并更新 min_sort_gap,但是现在多了操作 2,由于插入一个数只对它相邻的数的差值有变化,比如一开始是 1 3min_gap = abs(1-3 )= 2,在 1 后面插入 2 ,差值 “2” 没了,取而代之的是abs(1-2)abs(2-3),所以可以用一个堆 in 来维护当前序列的所有的差值,每次插入一个数,消失的差值存在另外一个堆 out 里,如果 in 的堆顶和 out 的堆顶相同,说明当前的min_gap已经消失,不断的pop掉堆顶直到堆顶元素不相同

并且我在极度无聊的情况下测试了下pbds的各种堆和STL的优先队列的速度(不开O2),发现还是有些差距的

在这里插入图片描述

#include<iostream>
#include<sstream>
#include<string>
#include<queue>
#include<map>
#include<unordered_map>
#include<set>
#include<vector>
#include<stack>
#include <utility>
#include<algorithm>
#include<cstdio>
#include<list>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<time.h>
#include<random>
using namespace std;
#include<ext/pb_ds/priority_queue.hpp>
#include<ext/pb_ds/tree_policy.hpp>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
using namespace __gnu_pbds;
#include<ext/rope>
#define int long long
using namespace __gnu_cxx;

#define PI acos(-1.0)
#define eps 1e-9
#define lowbit(a) ((a)&-(a))

const int mod = 1e9+7;
int qpow(int a,int b){
	int ans=1;
	while(b){
		if(b&1)ans=(ans*a)%mod;
		a=(a*a)%mod;
		b>>=1;
	}
	return ans;
}
const int INF = 0x3f3f3f3f;
const int N = 1e6+10;

mt19937 rng(233);
__gnu_pbds::priority_queue<int,greater<int>,pairing_heap_tag>in,out;
//这里用的是pb_ds的配对堆,在不开O2的情况下速度快于普通的优先队列
vector<int>g[500005];
//邻接表实现O(1)插入
unordered_map<int,int>mp;
//用hash表来记录每个数出现次数,假如>1,那么min_sort_gap永远为0
struct node{
	int l,r;
	int val,key;
}fhq[N];
int cnt,root,x,y,z;
int min_gap=INF,min_sort_gap=INF;

int newnode(int val){
	fhq[++cnt]={0,0,val,rng()};
	return cnt;
} 
void split(int now,int val,int &x,int &y){
	if(!now)x=y=0;
	else{
		if(fhq[now].val<=val){
			x=now;
			split(fhq[now].r,val,fhq[now].r,y);
		}
		else{
			y=now;
			split(fhq[now].l,val,x,fhq[now].l);
		}
	}
}
int merge(int x,int y){
	if(!x||!y) return x|y;
	if(fhq[x].key>fhq[y].key){
		fhq[x].r=merge(fhq[x].r,y);
		return x;
	}
	else{
		fhq[y].l=merge(x,fhq[y].l);
		return y;
	}
}
int get_pre(int val){//求前驱
	split(root,val-1,x,y);
	int now=x;
	while(fhq[now].r)now=fhq[now].r;
	root=merge(x,y);
	return fhq[now].val; 
}
int get_nxt(int val){//求后继
	split(root,val,x,y);
	int now=y;
	while(fhq[now].l)now=fhq[now].l;
	root=merge(x,y);
	return fhq[now].val;
}
int a[N];
#define endl '\n'
signed main(){
	std::ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	int n,q; cin>>n>>q;
	for(int i=1;i<=n;i++){
		int v; cin>>v;
		a[i]=v; mp[v]++;
		split(root,v,x,y);
		root=merge(merge(x,newnode(v)),y);
		g[i].push_back(v);
	}
	for(int i=2;i<=n;i++)in.push(abs(a[i]-a[i-1]));
	//记录所有的差值
	min_gap=in.top();
	sort(a+1,a+1+n);
	for(int i=2;i<=n;i++)min_sort_gap=min(min_sort_gap,abs(a[i]-a[i-1]));
	
	g[n+1].push_back(INF);
	split(root,INF,x,y); root=merge(merge(x,newnode(INF)),y);
	split(root,-INF,x,y); root=merge(merge(x,newnode(-INF)),y);
	//加两个最值防止溢出,邻接表也是
	string op;
	while(q--){
		cin>>op;
		if(op[0]=='I'){
			int p,val; cin>>p>>val;
			mp[val]++;
			if(mp[val]>1)min_sort_gap=0;
			int pre=g[p][g[p].size()-1];
			out.push(abs(pre-g[p+1][0]));//插入的前一项和后一项的差值消失
			in.push(abs(val-pre)); //插入元素与前一项的差值
			in.push(abs(val-g[p+1][0]));//插入元素与后一项的差值
			while(!out.empty()&&in.top()==out.top())in.pop(),out.pop();
			//找到最小且存在的相邻差值
			g[p].push_back(val);
			min_gap=in.top();
			if(!min_sort_gap)continue;
			//有两个相同元素,接下来不管怎么插入,min_sort_gap都是0
			split(root,val,x,y);
			root=merge(merge(x,newnode(val)),y);
			min_sort_gap=min(min_sort_gap,min(abs(get_pre(val)-val),abs(get_nxt(val)-val)));
		}
		if(op=="MIN_GAP")cout<<min_gap<<endl;
		if(op=="MIN_SORT_GAP")cout<<min_sort_gap<<endl;
		
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值