[luogu1110][ZJOI2007]报表统计【平衡树】
传送门
前言
洛谷和网上的题解都好复杂哦,或者是stl
水过。
窝的语文不怎么好,所以会有一些表达上的累赘或者是含糊不清,望各大佬海涵。
前置芝士
首先你一定要会平衡树(BST)。
什么平衡树都可以,只要是能过掉【模板】普通平衡树的都可以。
关于平衡树的详细操作这里就不一一赘述了。
正解
这一道题目看到的时候不能盲目思考能不能用一个数据结构一下子维护所有的正确答案,对于这一道题目是很难实现的。反正蒟蒻是实现不了
我们将这个问题一层一层的剖析一下。
第一个操作
插入操作,因为是在每一个原来的数后面插入一个数。
那么很容易想到用不定长数组vector
来实现。
因为这个满足vector
的结尾插入和不妨碍其他数组的性质。
第二个操作
查询相邻两个元素之间的差值(绝对值)的最小值。
比较容易想到,如果我们已经处理好了之前的答案,那么只有在插入的时候会影响这个答案。这句话很重要。
而且改变的肯定是一个vector
的队尾和下一个vector
的队首之间的答案。
那么我们就将所有队尾和下一个vector
的队首的差值维护一下。
但是有修改的操作,所以我们每一次插入一个数,那么会造成以下两种情况:
我们假设我们的数组是v[n][n]
的vector
,当前我们插入的数是第x
个数组,权值为Val
。每一个vector
的结尾是v[x].tail
,每一个vector
的头是v[x].head
。
- 减少一个答案,也就是
v[x].tail
和v[x+1].head
之间的答案 - 增加两个答案,也就是
v[x].tail
和k
和v[x+1].head
和k
之间的答案。
反观上面两种情况,因为我们是维护最小值,那么可以用可修改的堆来维护,但是我比较弱,又比较懒,就写了一个带插入和删除的Treap
,然后我们每一次需要输出答案的时候,只需要找到这个Treap
中的最小值,也就是一直往左儿子走就可以了。
第三个操作
这个操作就比较裸了,因为我们知道和当前这个数要差值最小,那么只有可能是他的前驱和后继。
这个又是平衡树可以实现的操作,那么我们只需要一个可以实现查找前驱和后继的BST
就可以了。窝还是用了Treap
来实现。
ps.需要注意下,因为我们第三个操作只是在修改的时候有操作,如果没有插入操作前,我们这个做法就是错的,那么就预处理排序一下,然后得到最小的差值就可以了
总结一下
- 操作1:
vector
暴力加入 - 操作2:
treap
维护差值 - 操作3:
treap
维护数值
复杂度分析
空间复杂度:\(O(n)\)
时间复杂度:\(O(nlogn)\)
代码复杂度:比较低。
思维难度:比较低。
代码
#include <bits/stdc++.h>
#pragma GCC optimize(2)
#define inf 0x3f3f3f3f
#define pb push_back
#define N 1000005
using namespace std;
template <typename T>
inline void read(T &x) {//快读不说
x = 0; T fl = 1; char ch = 0;
for (; ch < '0' || ch > '9'; ch = getchar())
if (ch == '-') fl = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar())
x = (x << 1) + (x << 3) + (ch ^ 48);
x *= fl;
}
vector<int> v[N];
map<int, int> mp;
struct Treap {//treap模板,有需要可以copy
int tot, rt;
struct node {
int val, ch[2], rd, cnt, sz;
void Init(int Val) { val = Val, rd = rand() % 233; sz = cnt = 1; ch[1] = ch[0] = 0; }
}tr[N];
void pushup(int nod) { tr[nod].sz = tr[tr[nod].ch[0]].sz + tr[tr[nod].ch[1]].sz + tr[nod].cnt; }
void rotate(int &nod, int d) {
int k = tr[nod].ch[d]; tr[nod].ch[d] = tr[k].ch[d ^ 1]; tr[k].ch[d ^ 1] = nod;
pushup(nod); pushup(k); nod = k;
}
void ins(int &nod, int val) {
if (!nod) { nod = ++ tot; tr[nod].Init(val); }
else {
tr[nod].sz ++;
if (tr[nod].val == val) { tr[nod].cnt ++; return; }
int d = val > tr[nod].val;
ins(tr[nod].ch[d], val);
if (tr[nod].rd > tr[tr[nod].ch[d]].rd) rotate(nod, d);
}
}
void del(int &nod, int val) {
if (!nod) return;
if (tr[nod].val == val) {
if (tr[nod].cnt > 1) { tr[nod].cnt --, tr[nod].sz --; return; }
int d = tr[tr[nod].ch[0]].rd > tr[tr[nod].ch[1]].rd;
if (!tr[nod].ch[1] || !tr[nod].ch[0]) nod = tr[nod].ch[1] + tr[nod].ch[0];
else rotate(nod, d), del(nod, val);
}
else tr[nod].sz --, del(tr[nod].ch[tr[nod].val < val], val);
}
int pre(int nod, int val) {
if (!nod) return -inf;
if (tr[nod].val > val) return pre(tr[nod].ch[0], val);
else return max(tr[nod].val, pre(tr[nod].ch[1], val));
}
int suc(int nod, int val) {
if (!nod) return inf;
if (tr[nod].val < val) return suc(tr[nod].ch[1], val);
else return min(tr[nod].val, suc(tr[nod].ch[0], val));
}
int Get_Min(int nod) {
if (!nod) return inf;
return min(tr[nod].val, Get_Min(tr[nod].ch[0]));
}
}tp, tp2;//两个treap
int ans, n, m;
char opt[5];
int a[N];
int main() {
srand(19260817);//随机种子
read(n); read(m); ans = inf;
for (int i = 1; i <= n; i ++) { v[i].clear(); read(a[i]); v[i].pb(a[i]); tp.ins(tp.rt, a[i]); }
for (int i = 2; i <= n; i ++) tp2.ins(tp2.rt, abs(v[i][0] - v[i - 1][0]));
sort(a + 1, a + 1 + n); for (int i = 2; i <= n; i ++) ans = min(ans, a[i] - a[i - 1]);//预处理出操作3的答案
for (int i = 1; i <= m; i ++) {
scanf("%s", opt);
if (opt[0] == 'I') {
int x, k; read(x); read(k);
int lst = tp.pre(tp.rt, k), nxt = tp.suc(tp.rt, k);
tp.ins(tp.rt, k); //插入当前的数
ans = min(ans, min(abs(lst - k), abs(nxt - k)));//查找前驱和后继来更新答案
tp2.del(tp2.rt, abs(v[x][(int)v[x].size() - 1] - v[x + 1][0])); //删除原先答案
tp2.ins(tp2.rt, abs(v[x][(int)v[x].size() - 1] - k));
tp2.ins(tp2.rt, abs(k - v[x + 1][0])); //加入现在答案
v[x].pb(k); //插入这个数
}
if (opt[4] == 'G') printf("%d\n", tp2.Get_Min(tp2.rt));//查找最小差值
if (opt[4] == 'S') printf("%d\n", ans);
}
return 0;
}
总结一下
这里我并没有把sort
看成stl
因为太习惯了。
我觉得吧,我并不是对stl
有偏见,虽然stl
在考场的时候可以救命,但是在平时的训练我还是尽可能的少用stl
,像平衡树这种东西,多敲敲除了费时间,对于自己的代码能力还是有帮助的。
自己层层剖析问题的能力还是需要加强。(我果然是一个蒟蒻)
这个程序在bzoj
上过不掉,好像超时了,但是在洛谷还是稳稳的过掉了。
[luogu1110][ZJOI2007]报表统计【平衡树】的更多相关文章
- luogu1110[ZJOI2007]报表统计
思路 这里的初始化就不讲了,看完操作讲解就应该明白了,再不行就去看代码 对于操作1 由于操作2的需要,vector[n]存下数 对于操作2的维护 查询相邻两个元素的之间差值(绝对值)的最小值 先把所有 ...
- bzoj P1058 [ZJOI2007]报表统计——solution
1058: [ZJOI2007]报表统计 Time Limit: 15 Sec Memory Limit: 162 MB Submit: 4099 Solved: 1390 [Submit][St ...
- 【BZOJ1058】[ZJOI2007]报表统计 STL
[BZOJ1058][ZJOI2007]报表统计 Description 小Q的妈妈是一个出纳,经常需要做一些统计报表的工作.今天是妈妈的生日,小Q希望可以帮妈妈分担一些工作,作为她的生日礼物之一.经 ...
- [ZJOI2007]报表统计(splay,堆)
[ZJOI2007]报表统计(luogu) Description 题目描述 Q的妈妈是一个出纳,经常需要做一些统计报表的工作.今天是妈妈的生日,小Q希望可以帮妈妈分担一些工作,作为她的生日礼物之一. ...
- bzoj1058: [ZJOI2007]报表统计
set.操作:insert(u,v)在u后面插入v,若u后面已插入过,在插入过的后面插入.mingap求出序列两两之间差值的最小值.minsortgap求出排序后的序列两两之间的最小值.用multis ...
- BZOJ 1058: [ZJOI2007]报表统计( 链表 + set )
这种题用数据结构怎么写都能AC吧...按1~N弄个链表然后每次插入时就更新答案, 用set维护就可以了... --------------------------------------------- ...
- [补档][ZJOI2007] 报表统计
[ZJOI2007] 报表统计 题目 传送门 小Q的妈妈是一个出纳,经常需要做一些统计报表的工作.今天是妈妈的生日,小Q希望可以帮妈妈分担一些工作,作为她的生日礼物之一. 经过仔细观察,小Q发现统计一 ...
- BZOJ_1058_[ZJOI2007]报表统计_STL
BZOJ_1058_[ZJOI2007]报表统计_STL Description 小Q的妈妈是一个出纳,经常需要做一些统计报表的工作.今天是妈妈的生日,小Q希望可以帮妈妈分担一些工 作,作为她的生日礼 ...
- bzoj 1058: [ZJOI2007]报表统计 (Treap)
链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1058 题面; 1058: [ZJOI2007]报表统计 Time Limit: 15 Sec ...
随机推荐
- python_超级基础
初识计算机 CPU 计算机的大脑.中央处理单元,主要负责数据运算及计算,是运算计算中心. 存储器 内存 临时存储数据,供CPU运算使用. 优点: 读取速度快. 缺点: 容量小,成本高,断电即消失. 硬 ...
- Linux之磁盘挂载
1.查看磁盘分区情况: fdisk -l 可以看到,红框中的硬盘没有分区. 2.开始分区: fdisk /dev/vdb 3.格式化分区: mkfs.xfs 分区名 4.挂载磁盘 挂载方式1: 手动挂 ...
- Access使用记录
iif函数 此函数类似编程语言中的双目运算符,官方解释如下: 在任何可以使用表达式的位置均可使用 IIf.您可以使用 IIf 确定另一个表达式为 True 还是 False.如果表达式为 True,则 ...
- Memcache之安装篇
本篇文章会介绍memcache在Windows和Linux下的具体安装过程,详细的记录其中的流程内容,帮助小伙伴们快速的搭建起memcache服务,废话少说,直接上!!! Windows: Memca ...
- # 【Python3练习题 007】 有一对兔子,从出生后第3个月起每个月都生一对兔子, # 小兔子长到第三个月后每个月又生一对兔子, # 假如兔子都不死,问每个月的兔子总数为多少?
# 有一对兔子,从出生后第3个月起每个月都生一对兔子,# 小兔子长到第三个月后每个月又生一对兔子, # 假如兔子都不死,问每个月的兔子总数为多少?这题反正我自己是算不出来.网上说是经典的“斐波纳契数列 ...
- [转帖]中关村:LED屏幕和OLED屏幕有什么区别?答案在这里
LED屏幕和OLED屏幕有什么区别?答案在这里 中关村在线 01-0810:40 目前的电视市场,更新换代的频率越来越快,无论是国产品牌还是合资品牌,都不约而同的推出了全新产品.这离不开人们对更好 ...
- sass变量引入全局
https://www.jianshu.com/p/ab9ab999344b(copy) 本文以Sass做案例,Less的参考,基本配置大同小异. 假如我们有一个Sass的全局变量common.scs ...
- C# Note2:委托(delegate) & Lambda表达式 & 事件(event)
前言 本文主要讲述委托和Lambda表达式的基础知识,以及如何通过Lambda表达式实现委托调用,并阐述.NET如何将委托用作实现事件的方式. 参考:C#高级编程 1.什么是委托(delegate)? ...
- (C/C++)区别:数组与指针,指针与引用
1.数组跟指针的区别 数组要么在静态存储区被创建(如全局数组),要么在栈上被创建.数组名对应着(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变. 指针可以随时指向任意类型 ...
- vue-cli项目开发/生产环境代理实现跨域请求+webpack配置开发/生产环境的接口地址
一.开发环境中跨域 使用 Vue-cli 创建的项目,开发地址是 localhost:8080,需要访问非本机上的接口http://10.1.0.34:8000/queryRole.不同域名之间的访问 ...