传送门

【洛谷传送门】
【bzoj传送门】

前言

洛谷和网上的题解都好复杂哦,或者是stl水过。
窝的语文不怎么好,所以会有一些表达上的累赘或者是含糊不清,望各大佬海涵。


前置芝士

首先你一定要会平衡树(BST)。
什么平衡树都可以,只要是能过掉【模板】普通平衡树的都可以。
关于平衡树的详细操作这里就不一一赘述了。


正解

这一道题目看到的时候不能盲目思考能不能用一个数据结构一下子维护所有的正确答案,对于这一道题目是很难实现的。反正蒟蒻是实现不了
我们将这个问题一层一层的剖析一下。

第一个操作

插入操作,因为是在每一个原来的数后面插入一个数。
那么很容易想到用不定长数组vector来实现。
因为这个满足vector的结尾插入和不妨碍其他数组的性质。

第二个操作

查询相邻两个元素之间的差值(绝对值)的最小值。
比较容易想到,如果我们已经处理好了之前的答案,那么只有在插入的时候会影响这个答案。这句话很重要。
而且改变的肯定是一个vector的队尾和下一个vector的队首之间的答案。
那么我们就将所有队尾和下一个vector的队首的差值维护一下。
但是有修改的操作,所以我们每一次插入一个数,那么会造成以下两种情况:
我们假设我们的数组是v[n][n]vector,当前我们插入的数是第x个数组,权值为Val。每一个vector的结尾是v[x].tail,每一个vector的头是v[x].head

  • 减少一个答案,也就是v[x].tailv[x+1].head之间的答案
  • 增加两个答案,也就是v[x].tailkv[x+1].headk之间的答案。

反观上面两种情况,因为我们是维护最小值,那么可以用可修改的堆来维护,但是我比较弱,又比较懒,就写了一个带插入和删除的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]报表统计【平衡树】的更多相关文章

  1. luogu1110[ZJOI2007]报表统计

    思路 这里的初始化就不讲了,看完操作讲解就应该明白了,再不行就去看代码 对于操作1 由于操作2的需要,vector[n]存下数 对于操作2的维护 查询相邻两个元素的之间差值(绝对值)的最小值 先把所有 ...

  2. bzoj P1058 [ZJOI2007]报表统计——solution

    1058: [ZJOI2007]报表统计 Time Limit: 15 Sec  Memory Limit: 162 MB Submit: 4099  Solved: 1390 [Submit][St ...

  3. 【BZOJ1058】[ZJOI2007]报表统计 STL

    [BZOJ1058][ZJOI2007]报表统计 Description 小Q的妈妈是一个出纳,经常需要做一些统计报表的工作.今天是妈妈的生日,小Q希望可以帮妈妈分担一些工作,作为她的生日礼物之一.经 ...

  4. [ZJOI2007]报表统计(splay,堆)

    [ZJOI2007]报表统计(luogu) Description 题目描述 Q的妈妈是一个出纳,经常需要做一些统计报表的工作.今天是妈妈的生日,小Q希望可以帮妈妈分担一些工作,作为她的生日礼物之一. ...

  5. bzoj1058: [ZJOI2007]报表统计

    set.操作:insert(u,v)在u后面插入v,若u后面已插入过,在插入过的后面插入.mingap求出序列两两之间差值的最小值.minsortgap求出排序后的序列两两之间的最小值.用multis ...

  6. BZOJ 1058: [ZJOI2007]报表统计( 链表 + set )

    这种题用数据结构怎么写都能AC吧...按1~N弄个链表然后每次插入时就更新答案, 用set维护就可以了... --------------------------------------------- ...

  7. [补档][ZJOI2007] 报表统计

    [ZJOI2007] 报表统计 题目 传送门 小Q的妈妈是一个出纳,经常需要做一些统计报表的工作.今天是妈妈的生日,小Q希望可以帮妈妈分担一些工作,作为她的生日礼物之一. 经过仔细观察,小Q发现统计一 ...

  8. BZOJ_1058_[ZJOI2007]报表统计_STL

    BZOJ_1058_[ZJOI2007]报表统计_STL Description 小Q的妈妈是一个出纳,经常需要做一些统计报表的工作.今天是妈妈的生日,小Q希望可以帮妈妈分担一些工 作,作为她的生日礼 ...

  9. bzoj 1058: [ZJOI2007]报表统计 (Treap)

    链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1058 题面; 1058: [ZJOI2007]报表统计 Time Limit: 15 Sec ...

随机推荐

  1. NET操作RabbitMQ组件EasyNetQ

    NET操作RabbitMQ组件EasyNetQ使用中文简版文档. 本文出自EasyNetQ官方文档,内容为自己理解加翻译.文档地址:https://github.com/EasyNetQ/EasyNe ...

  2. scrapy之持久化存储

    scrapy之持久化存储 scrapy持久化存储一般有三种,分别是基于终端指令保存到磁盘本地,存储到MySQL,以及存储到Redis. 基于终端指令的持久化存储 scrapy crawl xxoo - ...

  3. python--logging日志

    一个非常详细的日志使用请看这里:http://www.cnblogs.com/dkblog/archive/2011/08/26/2155018.html # 导入日志模块 import loggin ...

  4. 【Python3练习题 009】 打印出所有的“水仙花数”

    # [Python练习题 009] 打印出所有的“水仙花数”,所谓“水仙花数”是指一个三位数,# 其各位数字立方和等于该数本身.例如:153是一个“水仙花数”,# 因为153=1的三次方+5的三次方+ ...

  5. # 【Python3练习题 003】一个整数,它加上100后是一个完全平方数,再加上168又是一个完全平方数,请问该数是多少?

    # -------------------------------------------------## 所谓的“完全平方数”,就是开完根号仍然是整数.## 数学渣是这么思考的:假设这个数 i 在1 ...

  6. CLOUD物料列表查询的一份跟踪

    SELECT * FROM (SELECT t0.FNUMBER fnumber, t0_L.FNAME fname, t0_L.FSPECIFICATION fspecification, t0.F ...

  7. 校园电商项目2(基于SSM)——模块设计

    步骤一:各模块职责 步骤二:实体类设计 package com.figsprite.o2o.bean; import java.util.Date; public class Area { priva ...

  8. Python之路-(Django(csrf,中间件,缓存,信号,Model操作,Form操作))

    csrf 中间件 缓存 信号 Model操作 Form操作 csrf: 用 django 有多久,我跟 csrf 这个概念打交道就有久了. 每次初始化一个项目时都能看到 django.middlewa ...

  9. Windows开启WMI时一些总结

    通过远程的方式连接WMI获取计算机信息时,可能会出现远程主机拒绝访问,这时就要通过下面的方式来开启当前计算机的WMI服务,下面以Win7和Win10为例来进行相关的说明,通过一步步排查去连接远程服务. ...

  10. Visual Studio 2017 and Swagger: Building and Documenting Web APIs

    Swagger是一种与技术无关的标准,允许发现REST API,为任何软件提供了一种识别REST API功能的方法. 这比看起来更重要:这是一个改变游戏技术的方式,就像Web服务描述语言一样WSDL( ...