首页 > 其他分享 >P7907 [Ynoi2005] rmscne 题解

P7907 [Ynoi2005] rmscne 题解

时间:2023-11-18 21:33:21浏览次数:41  
标签:rmscne rt int 题解 位置 Ynoi2005 查询 lst 区间

P7907 [Ynoi2005] rmscne 题解

退役前的最后一篇题解,献给 Ynoi。再见了各位。

题目大意

给定一个长度为 \(n\) 的序列和 \(m\) 次查询,对于每次查询,给定 \(l, r\),求出一个最短的子区间 \([l', r']\),满足所有在区间 \([l, r]\) 中出现的数也在 \([l',r']\) 中出现过。

题解

题还是很好的。我讲的尽量细一点。

由于题解里所述的 \(q\) 与原题面重了,所以我们将原题面里的 \(q\) 次查询改成 \(m\) 次查询。

\(1 \le n, m \le 2 \times 10^6, 3s, 512 MB\)

先设一个东西:

\(S_{l, r}\) 表示区间 \([l, r]\) 里所有的数构成的集合。那么原题目的限制条件就可以转化为 \(S_{l, r} = S_{l', r'}\)。

没说强制在线,然后还是区间查询,那就考虑离线扫描线。

每扫到一个位置,处理右端点在这个位置的所有查询。

先来考虑简化版的问题:

给你一个区间 \([l, r]\) ,查询最短的满足 \(S_{l, r} = S_{l', r'}\) 的子区间 \([l', r']\) 的长度。

对于这个简化版问题,我们可以对每一个位置,维护左端点 \(l'\) 在这个位置时,最小的右端点 \(r'\) 以及其区间长度。但是我们发现,以某一些位置为左端点时,找不到对应的右端点,我们现在设法除去这些区间的干扰,不把它们算在答案的考虑范围之内,这方便我们一会儿解决原问题。设 \([q, r]\) 是以 \(r\) 为 \(r'\) 时,最短的满足条件的子区间。不难发现,将 \(i (i \in (q, r])\) 作为左端点 \(l'\) 时,是没有对应的右端点 \(r'\) 的。所以这个简化版问题的答案就是左端点为 \(i (i \in [l, q])\) 时,最短的区间长度。这个东西很线段树,不是么?

我们再来考虑原问题。

线段树维护对于一个点 \(i\),满足 \(S_{i, p} = S_{i, r}\)(\(r\) 为当前扫到的位置)的最小的 \(p\)。但是我们不需要这个 \(p\),我们只需要维护 \(p - i + 1\),即区间长度。

那么我们在处理区间 \([l, r]\) 的答案时,就是线段树上 \([l, q]\) 的最小值(\(q\) 就是上文所述的东西,即 \([q, r]\) 是以 \(r\) 为 \(r'\) 时,最短的满足条件的子区间)。

考虑这样求答案的正确性。

根据 \(q\) 的定义,\(S_{q, r} = S_{l, r}\),所以限制条件就可以转化成 \(S_{l', r'} = S_{l, r} = S_{q, r}\)。对于区间 \([l, q]\) 里的每一个 \(i\),\(S_{i, r} = S_{q, r}\) 这个条件一定满足,而对于区间 \((q, r]\) 里的每一个 \(i\),\(S_{i, r} = S_{q, r}\) 这个条件一定不满足(因为在位置 \(q\) 上的数,一定在 \(S_{q, r}\) 仅出现过一次,否则不优,\(q\) 可以更大)。

证毕。

所以我们现在的问题就转化成了,对于每一个 \(r\),求出对应的 \(q\),然后直接在线段树上查询即可。

查询就没了,现在讲修改。

考虑现在新扫到了一个位置 \(r + 1\),这个位置上的数为 \(a_{r + 1}\),我们记它上一次出现的位置为 \(lst\),那么对于区间 \([1, lst]\) 里的每一个 \(i\),\(S_{i, r} = S_{i, r + 1}\),不需要进行什么修改,而对于区间 \([lst + 1, r + 1]\) 里的每一个 \(i\),\(S_{i, r} \not= S_{i, r + 1}\),所以满足 \(S_{i, p} = S_{i, r}\) 的最小的 \(p\) 一定需要更新成 \(r + 1\)。

所以线段树部分就没了,就是一颗支持区间修改,区间最值查询的线段树,比较平凡。

我们考虑现在的复杂度是什么。

一共会进行 \(n\) 次修改,每次修改 \(O(\log n)\);一共有 \(m\) 次查询,每次查询需要找到一个 \(q\)(现在我们先二分找到这个 \(q\),一会儿再优化),然后再在线段树上查询,单次 \(O(\log n)\)。总的复杂度是 \(O(n \log n)\),但是常数似乎不小,过不去。

考虑优化。

考虑怎么对于每一个 \(r\) 迅速找到一个 \(q\)。

一个不太好想的优化,反正我没想到,看题解想到的。

考虑并查集优化,对于一个位置 \(i\),它的祖先就是,如果以这个点作为 \(q\),那么更优的 \(q\) 的位置,或者说我们先假定 \(i\) 为 \(q\),但是我们发现这个位置不优,于是它的祖先就指向了一个更优的位置。每一次新扫到一个位置 \(r + 1\),这个位置上的数为 \(a_{r + 1}\),我们记它上一次出现的位置为 \(lst\),那么我们将位置 \(lst\) 的祖先更新成 \(lst + 1\) 即可。我们最终用的 \(q\) 就是 \(l\) 位置的祖先。

为什么这么更新?

因为如果 \(q\) 在位置 \(lst\) 上,当前数与位置 \(lst\) 上的数相同,所以 \(lst\) 位置就没有用了,所以 \(lst + 1\) 就比 \(lst\) 更优了。

然后这道题基本就结束了。

整理一下:

  1. 离线扫描线;

  2. 每扫到一个点,线段树上区间修改,然后更新并查集;

  3. 处理右端点再当前位置的查询,每个查询的 \(q\) 就是 \(l\) 的祖先。

代码

#include <bits/stdc++.h>
#define P pair<int, int>
#define MP make_pair
using namespace std;

const int M = 2000005;
int n, q, a[M], ans[M], fa[M], lst[M];
int minn[M << 2], lazy[M << 2];
vector<P> vec[M];

inline int findf(int x) {
    while(x != fa[x]) 
        x = fa[x] = fa[fa[x]];
    return x;
}

inline void push_down(int rt, int l, int r) {
    if(lazy[rt]) {
        int mid = (l + r) >> 1, ls = rt << 1, rs = ls | 1;
        minn[ls] = lazy[rt] - mid + 1;
        minn[rs] = lazy[rt] - r + 1;
        lazy[ls] = lazy[rt];
        lazy[rs] = lazy[rt];
        lazy[rt] = 0;
    }
}

inline void push_up(int rt) {
    int ls = rt << 1, rs = ls | 1;
    minn[rt] = min(minn[ls], minn[rs]);
}

void build(int rt, int l, int r) {
    minn[rt] = INT_MAX;
    lazy[rt] = 0;
    if(l == r) 
        return;
    int mid = (l + r) >> 1, ls = rt << 1, rs = ls | 1;
    build(ls, l, mid);
    build(rs, mid + 1, r);
}

void change(int rt, int l, int r, int zuo, int you, int addend) {
    if(zuo <= l && r <= you) {
        minn[rt] = addend - r + 1;
        lazy[rt] = addend;
        return;
    }
    push_down(rt, l, r);
    int mid = (l + r) >> 1, ls = rt << 1, rs = ls | 1;
    if(zuo <= mid)
        change(ls, l, mid, zuo, you, addend);
    if(you > mid)
        change(rs, mid + 1, r, zuo, you, addend);
    push_up(rt);
}

int ask(int rt, int l, int r, int zuo, int you) {
    if(zuo <= l && r <= you) 
        return minn[rt];
    push_down(rt, l, r);
    int mid = (l + r) >> 1, ls = rt << 1, rs = ls | 1, res = INT_MAX;
    if(zuo <= mid)
        res = ask(ls, l, mid, zuo, you);
    if(you > mid)
        res = min(res, ask(rs, mid + 1, r, zuo, you));
    push_up(rt);
    return res;
}

signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    cin >> n;
    build(1, 1, n);
    for(int i = 1; i <= n; ++ i) 
        cin >> a[i], fa[i] = i;
    cin >> q;
    for(int i = 1; i <= q; ++ i) {
        int l, r;
        cin >> l >> r;
        vec[r].push_back(MP(l, i));
    }
    for(int i = 1; i <= n; ++ i) {
        fa[lst[a[i]]] = lst[a[i]] + 1;
        change(1, 1, n, lst[a[i]] + 1, i, i);
        for(int j = 0; j < vec[i].size(); ++ j) 
            ans[vec[i][j].second] = ask(1, 1, n, vec[i][j].first, fa[findf(vec[i][j].first)]);
        lst[a[i]] = i;
    }
    for(int i = 1; i <= q; ++ i) 
        cout << ans[i] << '\n';
}

标签:rmscne,rt,int,题解,位置,Ynoi2005,查询,lst,区间
From: https://www.cnblogs.com/Meteor-streaking/p/17841166.html

相关文章

  • P3412 仓鼠找sugar II 题解
    P3412仓鼠找sugarII题解大水题一个题目大意给定你一个树,设\(f_{u,v}\)表示在树上随机游走的情况下从\(u\)走到\(v\)的期望步数,求\(\displaystyle\frac{\sum_{i=1}^n\sum_{j=1}^nf_{i,j}}{n^2}\)。题解不难想到dp,不过\(1e5\)的范围差点让我怀疑我\(O(n......
  • P7775 [COCI2009-2010#2] VUK 题解
    链接这道题卡了我$40$多分钟。其实就是跑两遍广搜,第一遍算出每个点距离树的最小距离,第二遍开个优先队列,算出逃回窝的途中最大可能的离它最近的树的距离的最小值。接下来重点讲一下第二遍广搜。首先,我们要知道,如果我们用queue,那么最先到的点不一定是最优的。所以,我们需要......
  • CF391D1题解
    题目链接题意简述给出若干条平面上线段,找出最大的正+形边长多少。思路不难,但是判断两直线相交要考虑全面。数据不大不多,暴力直接过了。代码#include<bits/stdc++.h>usingnamespacestd;typedefstructline{intsx,sy;intex,ey;};intN,M;linexl[120......
  • [ABC326D] ABC Puzzle 题解
    题目链接解法分析这个问题是一个经典的排列谜题,通过回溯算法来穷举所有可能的字符排列,然后验证是否满足行和列约束。这个解决方案可以用于解决类似的谜题,其中需要满足一定的排列条件。通过仔细考虑约束条件,可以加快解决问题的速度,减少不必要的计算。更详细的我写在代码里了。......
  • P2678 跳石头 题解
    P2678跳石头链接这道题其实很水我们二分最长距离,最后用$check$函数判断合不合法一下是核心代码$check$函数这样写:boolcheck(intx){ intlast=0,tot=0; for(inti=1;i<=n;i++){ if(a[i]-last<x)tot++; elselast=a[i]; } if(len-last<x)tot++; returntot<......
  • CF1552D题解
    CF1552D题解思路首先,$a_i$的正负不重要,如果$a_i=b_j-b_k$,那么就有$-a_i=b_k-b_j$,读入时将$a_i$全部转化为正数。若满足$a_i+a_j+\ldots+a_k$,那么就可以构造出$b$序列,否则不行。从左到右遍历一遍$a$序列,动态规划推出所有可以组成的和,并判断是否满足上式,时间复杂度$O......
  • CF985C 题解
    CF985C题解思路由题意得知,现在有$n\timesk$块木板需要组装成$n$个木桶,每个木桶由$k$块板组成,容量服从短板原理,要求容量差不得超过$I$,求最大容量和。不管采用什么方法,无疑我们首先需要将板长(数组$a$)从小到大排列。利用贪心算法。先找出与$a_0$的长度差不超过$l$的......
  • UVA10652 Board Wrapping 题解
    LinkUVA10652BoardWrappingQuestion给出\(N\)个矩形,求面积最小的凸多边形能包住所有矩形求矩形面积占凸多边形面积的百分比Solution把矩形的四个顶点拿出来,就可以转化成凸包裸题了Code#include<bits/stdc++.h>usingnamespacestd;constdoubleeps=1e-9;constd......
  • NEFU OJ Problem1487 时空乱流题解
    时空乱流Problem:ETimeLimit:1500msMemoryLimit:65535KDescription星际飞行员Alice在一次航行中遭遇了时空乱流,时空乱流将导致Alice乘坐的飞船在n个位面之间穿梭。星际宇航局管理员Bob收到了Alice的求救信号,决定在某些位面上设立监测站,当Alice进入某个已经设立监......
  • 关于maven构建tomcat服务器一直启动不了的问题解决
    以往我都是创建maven项目后自己去配置web,再通过自己配置以下信息之后,配置tomcat。最终结果是始终报错 之后摸索发现,可以在maven项目配置前将以上“maven主路径,用户设置文件,本地仓库”信息确定好。 在弹出的页面设置好后,再创建maven项目便省时很多,tomcat配置后可以使用 ......