UOJ169. 【UR #11】元旦老人与数列

传送门
考虑用 \(segment~tree~beats\) 那一套理论,维护区间最小值 \(mn\) 和严格次小值 \(se\)
那么可以直接 \(mlog^2n\) 维护前三个操作
考虑维护历史最小值,先维护历史最小标记
写了写发现 \(max\) 那个修改不好操作
对于 \(max\) 操作来说,只会在 \(mn< v <se\) 的时候打上标记
这就相当于区间内等于 \(mn\) 的权值都要变成 \(v\)
那么 \(max\) 操作就可以变成对区间最小值的加法操作
而 \(v<se\),这样就可以非常方便维护历史最小值了
具体来说,维护下面几个标记

  1. 区间最小值的加法标记
  2. 区间其它值的加法标记
  3. 区间最小值的历史最小的加法标记
  4. 区间其它值的历史最小的加法标记
    下放的时候判断一下是否是区间最小值就好了
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;

namespace IO {
    const int maxn(1 << 21 | 1);

    char obuf[maxn], ibuf[maxn], *iS, *iT, c, *oS = obuf, *oT = obuf + maxn - 1, st[60];
    int f, tp;

    inline char Getc() {
        return iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, maxn, stdin), (iS == iT ? EOF : *iS++)) : *iS++;
    }

    template <class Int> inline void In(Int &x) {
        for (c = Getc(), f = 1; c < '0' || c > '9'; c = Getc()) f = c == '-' ? -1 : 1;
        for (x = 0; c >= '0' && c <= '9'; c = Getc()) x = (x << 1) + (x << 3) + (c ^ 48);
        x *= f;
    }

    inline void Flush() {
        fwrite(obuf, 1, oS - obuf, stdout);
        oS = obuf;
    }

    inline void Putc(char c) {
        *oS++ = c;
        if (oS == oT) Flush();
    }

    template <class Int> inline void Out(Int x) {
        if (x < 0) Putc('-'), x = -x;
        if (!x) Putc('0');
        while (x) st[++tp] = x % 10 + '0', x /= 10;
        while (tp) Putc(st[tp--]);
    }
}

using IO :: In;
using IO :: Out;
using IO :: Putc;
using IO :: Flush;

const int maxn(2e6 + 5);
const int inf(2e9);

struct Min {
    int mn1, mn2;

    inline Min operator +(Min b) const {
        Min c;
        c.mn1 = min(mn1, b.mn1), c.mn2 = min(mn2, b.mn2);
        if (b.mn1 ^ c.mn1) c.mn2 = min(c.mn2, b.mn1);
        if (mn1 ^ c.mn1) c.mn2 = min(c.mn2, mn1);
        return c;
    }
};

Min mn[maxn];
int n, m, hmn[maxn], addmn1[maxn], addmn2[maxn], addhmn1[maxn], addhmn2[maxn];

inline void Update(int x) {
    mn[x] = mn[x << 1] + mn[x << 1 | 1], hmn[x] = min(hmn[x << 1], hmn[x << 1 | 1]);
}

void Build(int x, int l, int r) {
    int mid;
    if (l == r) {
        In(mn[x].mn1), mn[x].mn2 = inf, hmn[x] = mn[x].mn1;
        return;
    }
    mid = (l + r) >> 1;
    Build(x << 1, l, mid), Build(x << 1 | 1, mid + 1, r);
    Update(x);
}

inline void Puttag(int x, int vmn1, int vmn2, int vhmn1, int vhmn2) {
    hmn[x] = min(hmn[x], mn[x].mn1 + vhmn1);
    addhmn1[x] = min(addhmn1[x], addmn1[x] + vhmn1);
    addhmn2[x] = min(addhmn2[x], addmn2[x] + vhmn2);
    addmn1[x] += vmn1, addmn2[x] += vmn2, mn[x].mn1 += vmn1;
    if (mn[x].mn2 ^ inf) mn[x].mn2 += vmn2;
}

inline void Pushdown(int x) {
    if (!addmn1[x] && !addmn2[x] && !addhmn1[x] && !addhmn2[x]) return;
    int ls, rs, now;
    ls = x << 1, rs = x << 1 | 1, now = min(mn[ls].mn1, mn[rs].mn1);
    if (now == mn[ls].mn1) Puttag(ls, addmn1[x], addmn2[x], addhmn1[x], addhmn2[x]);
    else Puttag(ls, addmn2[x], addmn2[x], addhmn2[x], addhmn2[x]);
    if (now == mn[rs].mn1) Puttag(rs, addmn1[x], addmn2[x], addhmn1[x], addhmn2[x]);
    else Puttag(rs, addmn2[x], addmn2[x], addhmn2[x], addhmn2[x]);
    addmn1[x] = addmn2[x] = addhmn1[x] = addhmn2[x] = 0;
}

void Modify_add(int x, int l, int r, int ql, int qr, int v) {
    int mid;
    if (ql <= l && qr >= r) {
        Puttag(x, v, v, v, v);
        return;
    }
    mid = (l + r) >> 1, Pushdown(x);
    if (ql <= mid) Modify_add(x << 1, l, mid, ql, qr, v);
    if (qr > mid) Modify_add(x << 1 | 1, mid + 1, r, ql, qr, v);
    Update(x);
}

void Modify_max(int x, int l, int r, int ql, int qr, int v) {
    int mid;
    if (mn[x].mn1 >= v) return;
    if (ql <= l && qr >= r && mn[x].mn2 > v) {
        Puttag(x, v - mn[x].mn1, 0, v - mn[x].mn1, 0);
        return;
    }
    mid = (l + r) >> 1, Pushdown(x);
    if (ql <= mid) Modify_max(x << 1, l, mid, ql, qr, v);
    if (qr > mid) Modify_max(x << 1 | 1, mid + 1, r, ql, qr, v);
    Update(x);
}

int Query_min(int x, int l, int r, int ql, int qr) {
    int mid, ret;
    if (ql <= l && qr >= r) return mn[x].mn1;
    mid = (l + r) >> 1, Pushdown(x), ret = inf;
    if (ql <= mid) ret = Query_min(x << 1, l, mid, ql, qr);
    if (qr > mid) ret = min(ret, Query_min(x << 1 | 1, mid + 1, r, ql, qr));
    Update(x);
    return ret;
}

int Query_hmin(int x, int l, int r, int ql, int qr) {
    int mid, ret;
    if (ql <= l && qr >= r) return hmn[x];
    mid = (l + r) >> 1, Pushdown(x), ret = inf;
    if (ql <= mid) ret = Query_hmin(x << 1, l, mid, ql, qr);
    if (qr > mid) ret = min(ret, Query_hmin(x << 1 | 1, mid + 1, r, ql, qr));
    Update(x);
    return ret;
}

int main() {
    int i, op, l, r, v;
    In(n), In(m), Build(1, 1, n);
    while (m) {
        --m, In(op), In(l), In(r);
        if (op == 1) In(v), Modify_add(1, 1, n, l, r, v);
        else if (op == 2) In(v), Modify_max(1, 1, n, l, r, v);
        else if (op == 3) Out(Query_min(1, 1, n, l, r)), Putc('\n');
        else Out(Query_hmin(1, 1, n, l, r)), Putc('\n');
    }
    return Flush(), 0;
}

原文地址:https://www.cnblogs.com/cjoieryl/p/10251754.html

时间: 2024-10-10 16:05:45

UOJ169. 【UR #11】元旦老人与数列的相关文章

【UOJ#169】元旦老人与数列

论文题. 考虑到这题的维护和区间操作是反向的,也就是说无法像V那题快速的合并标记. 我们知道,一个区间的最小值和其他值是可以分开来维护的,因为如果一个区间被整体覆盖,那么最小值始终是最小值. 对于被覆盖一半的区间,讨论一下即可. 对于每个最小值和次小值记录前缀最小值,当前/历史最小值,当到达合法区间的时候: 如果区间最小值>x,直接退出. 如果minv[o]<x<semn[o],那么更新当前的次小值 反之继续向下. #include<bits/stdc++.h> const

【uoj#51】[UR #4]元旦三侠的游戏 博弈论+dp

题目描述 给出 $n$ 和 $m$ ,$m$ 次询问.每次询问给出 $a$ 和 $b$ ,两人轮流选择:将 $a$ 加一或者将 $b$ 加一,但必须保证 $a^b\le n$ ,无法操作者输,问先手是否必胜. $n\le 10^9$ ,$m\le 10^5$ ,$a\ge 2$ ,$b\ge 1$ ,$a^b\le n$ 题解 博弈论+dp 显然可以想到预处理 $f[i][j]$ 表示 $a$ 为 $i$ ,$b$ 为 $j$ 时先手能否胜利.显然由 $f[i+1][j]$ 和 $f[i][j+

uoj167 元旦老人与汉诺塔(记忆化搜索)

QwQ太懒了,题目直接复制uoj的了 QwQ这个题可以说是十分玄学的一道题了 首先可以暴搜,就是\(dfs\)然后模拟每个过程是哪个柱子向哪个柱子移动 不多解释了,不过实现起来还是有一点点难度的 直接上代码吧 #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> using na

关于吉利线段树

引言 这玩意儿又称\(Segment\ Tree\ Beats\) .由吉老师.\(Picks\).美国队长三位知名毒瘤发明. 我的精神受到了污染...... 线段树简介 线段树什么的太难了,不是很会啊..... 线段树最强的地方在于,如果标记支持合并,那么我们就能够快速进行区间修改了. 一类特殊标记 维护这样一个标记\((a,b)\) ,表示\(x\to max(x + a , b)\) . 显然标记可合并:\((a,b) + (c,d) = (a + c , max(b + c , d))\

斐波那契数列——摘自搜狗百科

1数列公式 递推公式 斐波那契数列:0.1.1.2.3.5.8.13.21.34.55.89.144... 如果设F(n)为该数列的第n项(n∈N*),那么这句话可以写成如下形式: F(0) = 0,F(1)=F(2)=1,F(n)=F(n-1)+F(n-2) (n≥3) 通项公式 通项公式的推导方法一:利用特征方程 线性递推数列的特征方程为: X^2=X+1 解得 X1=(1+√5)/2, X2=(1-√5)/2. 斐波拉契数列则F(n)=C1*X1^n + C2*X2^n ∵F(1)=F(2

六十岁老人最后悔的那些事

六十岁老人最后悔的那些事 每个人都会有遗憾的事,我们来看看60岁老人心理最后悔的事是哪些? 1. 72%的老人后悔年轻时努力不够,以至事业无成. 2.67%的老人后悔选错了职业. 3.63%的老人后悔对子女教育不够或方法不当. 4.58%的老人后悔对健康重视不够,以至身体受损. 5.56%的老人后悔自己对老伴不够忠诚. 6.47%的老人后悔自己对双亲尽孝不够. 7.41%的老人后悔自己选错了终身伴侣. 8.36%的老人后悔自己未周游世界. 9.32%的老人后悔自己这一生过于平淡,缺乏刺激. 10

简述java递归与非递归算法,0-100求和,斐波那契数列,八皇后,汉诺塔问题

一:什么是递归算法? 递归算法就是直接或者间接的调用自己的方法,在达到一个条件的时候停止调用(递归出口),所以一定要找准好条件,让递归停止,否则就会是无限进行下去 二:递归程序设计的关键 1:找出调用中所需要的参数 2:返回的结果 3:递归调用结束的条件 三:递归程序注意 1:要有方法中自己调用自己 2:要有分支结构 3:要有结束的条件 四:简单叙述递归函数的优缺点 优点: 1:简洁清晰,实现容易,可读性好 2:在遍历的算法中,递归比循环更为简单 缺点: 1:效率低,使用递归函数是有空间和时间的

C++ STL 基础及应用(6) 容器

读者可能有这样的经历,自己编写了动态数组类.链表类.集合类和映射类等程序,然后小心地维护着.其实 STL 提供了专家级的几乎我们所需要的各种容器,功能更好,效率更高,复用性更强,所以开发应用系统应该首选 STL 容器类,摒弃自己的容器类,尽管它可能花费了你很多的开发时间. 本章将介绍 STL 中的通用容器 包括 vector.deque.list.queue和stack.priority_queue.bitset.set和multiset.map和multimap等等. 概述 容器分类 (1)序

递归的相关概念

几天没有更新,这两天是周末,给大家整理了一几篇东西,有关于作用域的,闭包的,还有递归的,闭包和递归,对于大部分初次接触编程的人来说还是有些难度的,昨天,花了一点时间给大家整理了一下,今天,给大家上传上来,让大家看看,部分属于个人观点,如有错误,欢迎指出 这一篇,给大家讲讲递归,昨天整理着三篇文章花了点时间,查了点资料,把自己的理解和大家说说,但是很多我也讲的不是很清楚,所以这一篇当中会用很多的小例子,小练习,给大家说说,希望可以给大家讲讲清楚. 1. 递归 1.1. 什么是递归 在程序中,所谓的