BZOJ4864[BeiJing 2017 Wc]神秘物质——非旋转treap
题目描述
21ZZ 年,冬。
小诚退休以后, 不知为何重新燃起了对物理学的兴趣。 他从研究所借了些实验仪器,整天研究各种微观粒子。这
一天, 小诚刚从研究所得到了一块奇异的陨石样本, 便迫不及待地开始观测。 在精密仪器的视野下,构成陨石
的每个原子都无比清晰。 小诚发现, 这些原子排成若干列, 每一列的结构具有高度相似性。于是,他决定对单
独一列原子进行测量和测试。被选中的这列共有 N 个顺序排列的原子。 最初, 第 i 个原子具有能量 Ei。 随着
时间推移和人为测试, 这列原子在观测上会产生两种变化:
merge x e 当前第 x 个原子和第 x+1 个原子合并,得到能量为 e 的新原子;
insert x e 在当前第 x 个原子和第 x+1 个原子之间插入一个能量为 e 的新原子。
对于一列原子,小诚关心的是相邻一段中能量最大和能量最小的两个原子的能量差值,
称为区间极差。 因此, 除了观测变化外,小诚还要经常统计这列原子的两类数据:
max x y 当前第 x 到第 y 个原子之间的任意子区间中区间极差的最大值;
min x y 当前第 x 到第 y 个原子之间的任意子区间中区间极差的最小值。
其中, 子区间指的是长度至少是 2 的子区间。
小诚坚信这项研究可以获得诺贝尔物理学奖。为了让小诚早日了结心愿,你能否帮助他实现上述的观测和测量呢?
输入
第一行, 两个整数 N, M, 分别表示最初的原子数目和事件总数。
第二行, N 个整数 E1, E2, …, EN, 由空格隔开。依次表示每个原子的能量。
接下来 M 行, 每行为一个字符串和两个整数, 描述一次事件,格式见题目描述。
N<=100,000,M<=100,000
1 ≤ e, Ei ≤ 109。 设 N’ 为当前时刻原子数目。
对于 merge 类事件, 1 ≤ x ≤ N’-1;
对于 insert 类事件, 1 ≤ x ≤ N’;
对于 max 和 min 类事件, 1 ≤ x < y ≤ N’。
任何时刻,保证 N’ ≥ 2。
输出
输出若干行, 按顺序依次表示每次 max 和 min 类事件的测量结果。
样例输入
4 3
5 8 10 2
max 1 3
min 1 3
max 2 4
5 8 10 2
max 1 3
min 1 3
max 2 4
样例输出
5 2 8
题目需要维护一个序列要求能删除、插入、求区间最大最小极差。
用一个平衡树维护即可区间信息及插入删除即可。
对于区间最大极差:因为将区间扩大只会使最小值更小、最大值更大,所以最大极差就是区间最小值与最大值的差。维护区间最大值、最小值即可。
对于区间最小极差:同样因为扩大区间会使极差变大,所以最小极差就是相邻两数的差的最小值。维护每个点与前一个点的差及区间相邻点差的最小值即可。
每一次插入或删除时要改变后继与前一个点的差(因为它前一个点变了)。
注意第一个点与前一个点的差是INF。
代码不是很清真。
#include<set> #include<map> #include<queue> #include<stack> #include<cmath> #include<vector> #include<bitset> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define pr pair<int,int> #define ll long long using namespace std; int ls[200010]; int rs[200010]; int r[200010]; int v[200010]; int mn[200010]; int size[200010]; int mx[200010]; int s[200010]; int t[200010]; int n,m; char ch[10]; int x,y; int cnt; int a,b,c,d; int root; int build(int val) { int rt=++cnt; v[rt]=val; r[rt]=rand(); size[rt]=1; mx[rt]=mn[rt]=val; ls[rt]=rs[rt]=0; t[rt]=s[rt]=1e9+7; return rt; } void pushup(int rt) { size[rt]=size[ls[rt]]+size[rs[rt]]+1; mx[rt]=max(max(mx[ls[rt]],mx[rs[rt]]),v[rt]); mn[rt]=min(min(mn[ls[rt]],mn[rs[rt]]),v[rt]); t[rt]=min(min(t[ls[rt]],t[rs[rt]]),s[rt]); } int merge(int x,int y) { if(!x||!y) { return x+y; } if(r[x]<r[y]) { rs[x]=merge(rs[x],y); pushup(x); return x; } else { ls[y]=merge(x,ls[y]); pushup(y); return y; } } void split(int rt,int &x,int &y,int k) { if(!rt) { x=y=0; return ; } if(k<=size[ls[rt]]) { y=rt; split(ls[rt],x,ls[y],k); pushup(rt); } else { x=rt; split(rs[rt],rs[x],y,k-size[ls[rt]]-1); pushup(rt); } } void ins(int k,int val) { split(root,a,b,k-1); split(b,b,c,1); split(c,c,d,1); int now=build(val); t[now]=s[now]=abs(v[now]-v[b]); if(b==0) { t[now]=s[now]=1e9+7; } t[c]=s[c]=abs(v[c]-v[now]); if(c==0) { t[c]=s[c]=1e9+7; } a=merge(a,b); a=merge(a,now); a=merge(a,c); root=merge(a,d); } void del(int k,int val) { split(root,a,b,k-1); split(b,b,c,1); split(c,c,d,1); root=merge(a,d); ins(k-1,val); } int query_max(int l,int r) { split(root,a,c,r); split(a,a,b,l-1); int now=mx[b]-mn[b]; root=merge(merge(a,b),c); return now; } int query_min(int l,int r) { l++; split(root,a,c,r); split(a,a,b,l-1); int now=t[b]; root=merge(merge(a,b),c); return now; } int main() { srand(12378); mx[0]=0; mn[0]=1e9+7; t[0]=1e9+7; v[0]=1e9+7; s[0]=1e9+7; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",&x); if(i==1) { root=build(x); t[root]=s[root]=1e9+7; } else { ins(i-1,x); } } while(m--) { scanf("%s",ch); scanf("%d%d",&x,&y); if(ch[1]=='e') { del(x,y); } else if(ch[1]=='n') { ins(x,y); } else if(ch[1]=='a') { printf("%d\n",query_max(x,y)); } else { printf("%d\n",query_min(x,y)); } } }