首页 > 其他分享 >AT_past202107_l 题解

AT_past202107_l 题解

时间:2024-02-02 22:33:48浏览次数:31  
标签:past202107 递归 minn int 题解 mid 最小值 区间

Solution

题目来源:AT_past202107_l(in AtCoder | in luogu

用线段树维护区间最小值。单点修改很好写,我们看怎么区间寻找最小值位置。

对于每次询问,我们先求出所查询区间 \([x_i, y_i]\) 的最小值 \(p\),然后写一个寻找函数。对于当前区间 \([l, r]\),分以下情况来看:

  1. 如果当前区间长度 \(> 1\)、并且并不包含在所查询区间内(即 \(l < x_i\) 或 \(r > y_i\)),我们像正常线段树一样递归查找其在所查询区间内的左右子区间。
  2. 如果当前区间长度 \(> 1\)、并且在查询区间内(即 \(x_i \le l\) 且 \(r \le y_i\)),则该区间的最小值至少为 \(p\)。我们递归寻找其最小值为 \(p\) 的左右子区间即可。
  3. 如果当前区间长度恰为 \(1\),则若当前位置的数是 \(p\),则该位置为答案,统计起来。

这部分代码如下:

void fnd(int l, int r, int k, int L, int R, ll x) {
    if(l == r) {
        if(minn[k] == x) v.push_back(l);
        return;
    }
    int mid = (l + r) >> 1;
    if(L <= l && r <= R) {
        if(minn[ls(k)] == x) fnd(l, mid, ls(k), L, R, x);
        if(minn[rs(k)] == x) fnd(mid + 1, r, rs(k), L, R, x); 
        return;
    }
    if(L <= mid) fnd(l, mid, ls(k), L, R, x);
    if(mid < R) fnd(mid + 1, r, rs(k), L, R, x);
}

为啥情况 \(3\) 要加粗「当前位置的数是 \(p\)」呢?我们可以发现,存在查询区间 \([x_i, y_i]\) 与当前区间 \([l, r]\) 仅交于一个数的情况。这样递归下去会发现,我们最后递归到了那个相交的位置,但这个位置却不一定是 \(p\)。所以我们要特判一下。

不过这个递归过程还是有点麻烦了,可以参考 @SamHH0902 的实现方式。虽然我感觉我的更好想一点。

建议参考代码理解。code:

#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#define ls(a) ((a) << 1)
#define rs(a) ((a) << 1 | 1)
using namespace std;

#define ll long long
#define INF 2e9
const int N = 200010;
int n, q;
ll a[N];
ll minn[N * 4];
vector<ll> v;

void pushup(int k) {
    minn[k] = min(minn[ls(k)], minn[rs(k)]);
}

void build(int l, int r, int k) {
    minn[k] = INF;
    if(l == r) {
        minn[k] = a[l];
        return;
    }
    int mid = (l + r) >> 1;
    build(l, mid, ls(k)), build(mid + 1, r, rs(k));
    pushup(k);
}

void upd(int l, int r, int k, int x, ll c) {
    if(l == r) {
        minn[k] = c;
        return;
    }
    int mid = (l + r) >> 1;
    if(x <= mid) upd(l, mid, ls(k), x, c);
    else upd(mid + 1, r, rs(k), x, c);
    pushup(k);
}

ll query(int l, int r, int k, int L, int R) {
    if(L <= l && r <= R) {
        return minn[k];
    }
    int mid = (l + r) >> 1; ll res = INF;
    if(L <= mid) res = min(res, query(l, mid, ls(k), L, R));
    if(mid < R) res = min(res, query(mid + 1, r, rs(k), L, R));
    return res;
}

void fnd(int l, int r, int k, int L, int R, ll x) {
    if(l == r) {
        if(minn[k] == x) v.push_back(l);
        return;
    }
    int mid = (l + r) >> 1;
    if(L <= l && r <= R) {
        if(minn[ls(k)] == x) fnd(l, mid, ls(k), L, R, x);
        if(minn[rs(k)] == x) fnd(mid + 1, r, rs(k), L, R, x); 
        return;
    }
    if(L <= mid) fnd(l, mid, ls(k), L, R, x);
    if(mid < R) fnd(mid + 1, r, rs(k), L, R, x);
}

int main() {
    scanf("%d%d", &n, &q);
    for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
    build(1, n, 1);
    while(q--) {
        int op; scanf("%d", &op);
        if(op == 1) {
            int x, y; scanf("%d%d", &x, &y);
            upd(1, n, 1, x, y);
        } else {
            int l, r; scanf("%d%d", &l, &r);
            v.clear();
            ll m = query(1, n, 1, l, r);
            fnd(1, n, 1, l, r, m);
            sort(v.begin(), v.end());
            v.erase(unique(v.begin(), v.end()), v.end());
            printf("%d ", (int) v.size());
            for (int i = 0; i < (int)v.size(); i++) printf("%lld ", v[i]);
            printf("\n");
        }
    }
    return 0;
}

标签:past202107,递归,minn,int,题解,mid,最小值,区间
From: https://www.cnblogs.com/Running-a-way/p/18004129/sol-AT_past202107_l

相关文章

  • CF1542E2 题解
    一、题目描述:设$\pi(x)$为全排列$x$的逆序对数。给定$n,m$,求有多少对长度为$n$ 的排列$p,q$,使得$p$的字典序小于$q$,且$\pi(p)>\pi(q)$答案对$m$取模。数据范围:$1\len\le500,1\lem\le10^9$。 二、解题思路:一开始列出计算式个人感觉是......
  • 230718B3-path 题解
    感谢cn_ryh大佬的怂恿(否则我真不会动这个题感谢cszhpdx的指导帮助qwq(让我们膜拜一下场切的浩杨哥orz解决这个题让人很有成就感(题意给定一个基环树,边有长度l、限速v、价值w(每单位时间)已知起点s、终点t、最高速度u,求最小花费边数、询问次数$10^5$解法首先学习一下基......
  • 基于客户真实使用场景的云剪辑Timeline问题解答与代码实操
    本文为阿里云智能媒体服务IMS「云端智能剪辑」实践指南第6期,从客户真实实践场景出发,分享一些Timeline小技巧(AI_TTS、主轨道、素材对齐),助力客户降低开发时间与成本。欧叔|作者故事的开始要从一条客户的真实反馈说起。  Round1:视频比音频长,怎么办?某天,一位客户加入了智能媒......
  • 盘点那些硬件+项目学习套件:Hi3861鸿蒙开发板及入门常见问题解答
    华清远见20岁了~过去3年里,华清远见研发中心针对个人开发板业务,打造了多款硬件+项目学习套件,涉及STM32单片机、嵌入式、物联网、人工智能、鸿蒙、ESP32、阿里云IoT等多技术方向。今天我们来盘点一下,比较受欢迎几款“硬件+项目”学习套件,以及一些初学者比较关注的问题。盘点二:Hi3861......
  • fatal: couldn't find remote ref master 问题解决!
    这个错误信息通常出现在使用Git命令尝试从远程仓库克隆、拉取(pull)或推送(push)时,指定的分支(在这个案例中是master)在远程仓库中不存在。这种情况可能由以下几个原因导致:1.分支名称错误远程仓库中不存在名为master的分支:随着Git和GitHub的更新,master分支被重新命名为main......
  • AcWing 520. 子串 题解
    ps:觉得这编号很特殊就做了一下题目传送门算法(线性DP,前缀和)\(O(nmk)\)首先考虑如何DP,然后再考虑如何优化。状态表示:f[i,j,k]表示只用S的前i个字母,选取了k段,可以匹配T的前j个字母的方案数。状态计算:将f[i,j,k]表示的所有方案分成两大类:不用S[i],则方案数是f[i-1,......
  • P9309 [EGOI2021] Number of Zeros题解
    模拟赛时以为是进位制的题目,结果还做出来了。此题解解法与其它相似,但观察的角度不同(作者的脑回路不同)。此题问\(a\simb\)的最小公倍数中后导\(0\)的个数,即求其中\(2\)和\(5\)个数的最小值。分别计算即可,想到进位制,以\(a=10\),\(b=12\)时\(2\)的个数为例:1001(9)......
  • P9612 [CERC2019] Light Emitting Hindenburg 题解
    题目传送门题目大意这个题目简化一下就是求\(n\)个数中取\(k\)个数按位与的最大值思路很容易想到贪心。题中说道输入的数在二进制下最多\(29\)位,所以我们从\(29\)开始遍历二进制位,如果当前位有大于等于\(k\)个\(1\),那么标记一下这些数,可以发现剩下的比当前位低的......
  • AcWing 3721. 求30的倍数 题解
    传送门stoi这里我们来了解一个函数\(stoi\)作用是将\(n\)进制的字符串转化为十进制,使用时包含头文件\(string\)定义如下:intstoi(conststd::string&str,std::size_t*pos=nullptr,intbase=10);参数:\(str\)-待转换的字符\(pos\)-其取值可以是一个空字......
  • CF1687D Cute number 题解
    题目链接点击打开链接题目解法一个数\(c\)是可爱(下文称为合法)的,当且仅当\(c\in[x^2,x(x+1)]\)令\(a_i\)属于\([x_i,x_i(x_i+1)]\)一个显然的范围是\(x_1\le2\times10^6\)考虑枚举\(x_1\)现在说明一个结论:\(x_1\)固定时,\(x_i\)也固定了说明:不难发现,合法的不合......