[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 ...
随机推荐
- Druid Monitor开启登录界面
<!-- druid --> <filter> <filter-name>druidWebStatFilter</filter-name> <fi ...
- oracle小记:dba_data_files
今天给表空间扩展的时候,使用了dba_data_files进行查询.查阅了网上的资料. 该系统系统中含有以下字段 每个字段的含义如下:
- WPF中定时器Timer与DispatcherTimer的用法
最近的工作项目中需要定时更新UI控件中的数据,这时候第一反应肯定会想到去使用System.Timers.Timer定时更新UI控件,但是程序运行后,会发现程序崩溃了.报的异常为“调用线程无法访问此对象 ...
- JSON Support in PostgreSQL and Entity Framework
JSON 和JSONB的区别(What's difference between JSON and JSONB data type in PosgresSQL?) When should be use ...
- linux中的set -e 与set -o pipefail
1.set -e "Exit immediately if a simple command exits with a non-zero status." 在“set -e”之后出 ...
- requests 使用免费的代理ip爬取网站
import requests import queue import threading from lxml import etree #要爬取的URL url = "http://xxx ...
- Gatsby & React & NPX & NVM
Gatsby & React Gatsby is a blazing fast modern site generator for React. https://www.gatsbyjs.or ...
- loadrunner -vuser
在每个负载生成器上,安装 remote agent dispatcher(process) 和 loadrunner agent 控制器指示remote agent dispatcher 在load ...
- Delphi (Library Path Browsing Path)
首先要明白的一个概念是dcu文件 *.dcu是*.pas的编译后单元文件(Delphi Compiled Unit), 编译器把它和库文件连接起来就构成了可执行文件*.exe 或*.dll等,相当于C ...
- LoadRunner Vuser接口测试脚本 Post举例
发送内容为JSON的POST请求需要加入"EncType=application/json",表单参数不需要.(JAVA API) // POST接口调用 web.custom_r ...