题目链接:rmscne

神仙经典数据结构难题。看到求区间种类数有关的东西,需要下意识的反应到经典老题 HH的项链,这里可以学习我这篇 题解。具体学习下扫描线怎么做这类东西的。

看看本题,首先处理区间查询问题,而且是这种很复杂的子区间问题。这里的 \(l'\) 和 \(r'\) 所组成的子区间 \([l',r']\) 实在太多了,我们可以先思考一个弱化问题版本。固定某一个端点再询问。

我们固定 \(r'=r\),考虑对于某个 \(r\) 来说,此时此刻的 \(l'\) 为满足答案的点,换句话来说,\([l',r]\) 为 \([l,r]\) 的弱化版答案。我们来考虑一波 \(r\) 的移动会带来的影响,使用 HH 的项链的经典 \(last\) 分析,确保你会这个东西,否则需要看我上面说的那篇题解。

当 \(r+1\) 以后,区间会增加一个数 \(x\) 我们采用最经典得到 \(last_x\) 分析这个 \(x\) 导致的变化,很显而易见的是,如果 \(x\) 恰好为 \(l'\) 对应的点。那么很容易知道 HH 的项链里面的核心思想:后面新出行的 \(new_x\) 会代替掉原本的 \(last_x\) 的贡献,这个时候 \(l'\) 会向右移动。考虑一般情况,对于任意一个 \(last_x\) 显然在更新的时候都该做一个 \(last_x \rightarrow last_x +1\) 的操作。意思是如果我对于当前的 \(r\) 由于它有一个 \(x\),那么我的答案不需要包含这个 \(last_x\) 作为 \(l'\),向右移动一位,如果有多个肯定向右移动的不止一位了。所以这个问题也是需要考虑的。但总而言之,我们现在至少解决了一类问题 \([l,r]\) 中国如果右端点固定了,我们可以清楚地知道 \([l',r]\) 作为 \([l,r]\) 的最小被包含区间的最右 \(l'\) 的计算方法。

核心点的提出

那么我们反过来考虑固定的是 \(l\), 也即是 \(l'\)。它的 \(r'\) 是否也那么好找。我们注意到一个问题,这个 \(l'\) 并不是确定的,所以我们需要把每个点都认为是查询点 \(l=l'\) 去看。我们来考虑画出若干个点在 \(r\) 移动到 \(r+1\) 的变化。

我们观察若干个 \(l_i\) 的情况,我们很容易知道有个性质,\(r_i\) 一定单独调不降。因为 \(l_i\) 每移动一位就会少一个数,如果种类少了,需要后面的 \(r_i\) 移动进行补数操作。所以 \(r_i\) 不会下降的。再来一遍经典的 \(last_x\) 思想,很容易知道 \((last_x,r]\) 这个区间上已经没有 \(x\) 了,需要把 \(r+1\) 对应的 \(x\) 用于弥补,也就是后者代替前者贡献的基本思想。那么会有哪些 \(l'\) 需要包括这个新的 \(r+1\) 呢,显然是 \((last_x,r]\) 范围内的,因为他们都需要这个 \(x\) 进行补充。那么区改?覆盖?这不线段树吗?这样我们就可以做到同时维护一堆 \(l'\) 的前缀答案。我们惊讶地发现对于一个区间 \([l,r]\) 我们这样拆分:

\[[l,r]、[l+1,r],[l+2,r]....[l+diff,r],其中可以通过第一点有[l+diff,r]是
\]
\[最小的[l,r] 上的后缀答案,具体的获取后续说如何实现,它们每一个的 [l_i,r] 的答案 [l_i,r_i]
\]
\[恰好又是第二点提到的所有的前缀最终答案,易知答案为 min(r_i-l_i+1)(前缀最小)
\]

重新阐述下,首先呢答案 \([l',r']\) 既然满足 \([l,r]\) 的条件,那么也一定满足它的子区间,把它拆成若干个 \([l_i,r]\) 很显然,最终答案一定是某个左端点为 \(l_i\) 的,而对应答案的 \([l_{ans},r]\) 这个后缀区间是最长的,连它都不满足 \([l,r]\) 的前提,那就更不可能更小区间了。

如图所示,假如 \([l_i,r_i]\) 是答案,那么它对应的后缀区间也一定是答案。而由于后缀区间随 \(l_i\) 增大而减小长度,所以越不容易满足条件的,这个显然是单调的,一定有一个 \(l_{max}\) 为最大的 \(l_i\) 点。然后答案很显而易见的就是这个小区间上的前缀区间,这个不就是我们说的第二点吗?

至此,大体已经讲清,正式阐述细节和算法核心思路。

算法逻辑与框架

  1. 首先若干个区间查询很麻烦,我们能不能只抽象成单点移动,显然参照 HH 的项链,我们只需要离线扫描线查询,从 \(1 \sim n\) 更新 \(r\) 再查询对应时刻答案即可。

  2. 对于 \(r\) 的移动,基于第一点,我们会发现它所更新的 \(last_x\) 也会向右移动一位,换句话来讲这可以看做区间合并的过程,你想到了什么?经典的并查集合加快合并区间的套路。为每个点维护一个当前能快速移动到的位置,这个位置是什么东西呢?让我们来想一下,点 \(l\) 能移动到 \(L\) 处在此时 \(r\) 时刻。那么显而易见的是在更新 \(r\) 的过程中在 \([l,L]\) 中,它都更新过 \(last_x\) ,最新的 \(L\) 处 \(last_x\) 还未被后面覆盖,如果我们在往右移动,岂不是没有补的了?wow 这个就是我们要找的那个最大的 \(l_{max}\)。

  3. 考虑第二点说的,当 \(r\) 移动,我们需要把一系列点的 \(r'\) 改为当前的 \(r\),显而易见的是需要区间覆盖,然后我们还需要查询基于上述的 \([l,l_{max}]\) 范围内所有 \(l_i\) 后缀对应的答案前缀,也即是 \(\min(r_i-l_i+1)\),这玩意不就是区间最小值查询吗?所以线段树维护即可,具体的维护区间最小值,区间覆盖,它们的右端点都是需要覆盖成 \(r\) ,所以考虑左端点最大就行,每次覆盖更新的时候容易区间最小值一定为区间最右边的点到当前的 \(r\) 处,如图所示。

显然最小的区间最右边的点到当前点。然后注意区间合并并查集是向右合并,至此,全部完毕。数据量 \(2e6\),所以可以考虑上快读其实。

参照代码
#include <bits/stdc++.h>

//#pragma GCC optimize("Ofast,unroll-loops")

#define isPbdsFile

#ifdef isPbdsFile

#include <bits/extc++.h>

#else

#include <ext/pb_ds/priority_queue.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/trie_policy.hpp>
#include <ext/pb_ds/tag_and_trait.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/list_update_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/exception.hpp>
#include <ext/rope> #endif using namespace std;
using namespace __gnu_cxx;
using namespace __gnu_pbds;
typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef tuple<int, int, int> tii;
typedef tuple<ll, ll, ll> tll;
typedef unsigned int ui;
typedef unsigned long long ull;
typedef __int128 i128;
#define hash1 unordered_map
#define hash2 gp_hash_table
#define hash3 cc_hash_table
#define stdHeap std::priority_queue
#define pbdsHeap __gnu_pbds::priority_queue
#define sortArr(a, n) sort(a+1,a+n+1)
#define all(v) v.begin(),v.end()
#define yes cout<<"YES"
#define no cout<<"NO"
#define Spider ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
#define MyFile freopen("..\\input.txt", "r", stdin),freopen("..\\output.txt", "w", stdout);
#define forn(i, a, b) for(int i = a; i <= b; i++)
#define forv(i, a, b) for(int i=a;i>=b;i--)
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define endl '\n'
//用于Miller-Rabin
[[maybe_unused]] static int Prime_Number[13] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37}; template <typename T>
int disc(T* a, int n)
{
return unique(a + 1, a + n + 1) - (a + 1);
} template <typename T>
T lowBit(T x)
{
return x & -x;
} template <typename T>
T Rand(T l, T r)
{
static mt19937 Rand(time(nullptr));
uniform_int_distribution<T> dis(l, r);
return dis(Rand);
} template <typename T1, typename T2>
T1 modt(T1 a, T2 b)
{
return (a % b + b) % b;
} template <typename T1, typename T2, typename T3>
T1 qPow(T1 a, T2 b, T3 c)
{
a %= c;
T1 ans = 1;
for (; b; b >>= 1, (a *= a) %= c)if (b & 1)(ans *= a) %= c;
return modt(ans, c);
} template <typename T>
void read(T& x)
{
x = 0;
T sign = 1;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-')sign = -1;
ch = getchar();
}
while (isdigit(ch))
{
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
x *= sign;
} template <typename T, typename... U>
void read(T& x, U&... y)
{
read(x);
read(y...);
} template <typename T>
void write(T x)
{
if (typeid(x) == typeid(char))return;
if (x < 0)x = -x, putchar('-');
if (x > 9)write(x / 10);
putchar(x % 10 ^ 48);
} template <typename C, typename T, typename... U>
void write(C c, T x, U... y)
{
write(x), putchar(c);
write(c, y...);
} template <typename T11, typename T22, typename T33>
struct T3
{
T11 one;
T22 tow;
T33 three; bool operator<(const T3 other) const
{
if (one == other.one)
{
if (tow == other.tow)return three < other.three;
return tow < other.tow;
}
return one < other.one;
} T3() { one = tow = three = 0; } T3(T11 one, T22 tow, T33 three) : one(one), tow(tow), three(three)
{
}
}; template <typename T1, typename T2>
void uMax(T1& x, T2 y)
{
if (x < y)x = y;
} template <typename T1, typename T2>
void uMin(T1& x, T2 y)
{
if (x > y)x = y;
} constexpr int N = 2e6 + 10;
constexpr int INF = 1e9 + 7; struct Node
{
int cov;
int len;
} node[N << 2]; #define cov(x) node[x].cov
#define len(x) node[x].len
int n;
//区间答案最小值
inline void push_up(const int curr)
{
len(curr) = min(len(ls(curr)),len(rs(curr)));
} inline void push_down(const int curr, const int l, const int r)
{
const int mid = l + r >> 1;
if (cov(curr))
{
cov(ls(curr)) = cov(rs(curr)) = cov(curr);
//[l,mid] [mid+1,r] cov(curr) 最小值为右端点到当前点
len(ls(curr)) = cov(curr) - mid + 1;
len(rs(curr)) = cov(curr) - r + 1;
cov(curr) = 0;
}
} inline void cover(const int curr, const int l, const int r, const int val, const int s = 1, const int e = n)
{
//用区间右端点更新
if (l <= s and e <= r)
{
cov(curr) = val;
len(curr) = val - e + 1;
return;
}
const int mid = s + e >> 1;
push_down(curr, s, e);
if (l <= mid)cover(ls(curr), l, r, val, s, mid);
if (r > mid)cover(rs(curr), l, r, val, mid + 1, e);
push_up(curr);
} //查询最小值
inline int query(const int curr, const int l, const int r, const int s = 1, const int e = n)
{
if (l <= s and e <= r)
{
return len(curr);
}
const int mid = s + e >> 1;
push_down(curr, s, e);
int ans = n + 1;
if (l <= mid)uMin(ans, query(ls(curr), l, r, s, mid));
if (r > mid)uMin(ans, query(rs(curr), l, r, mid + 1, e));
return ans;
} int fa[N]; inline int find(const int x)
{
return x == fa[x] ? x : fa[x] = find(fa[x]);
} int last[N];
vector<pii> child[N];
int q;
int a[N], ans[N]; inline void solve()
{
read(n);
forn(i, 1, n)read(a[i]), fa[i] = i;
read(q);
forn(i, 1, q)
{
int l, r;
read(l, r);
child[r].emplace_back(l, i);
}
forn(r, 1, n)
{
int& preLast = last[a[r]];
cover(1, preLast + 1, r, r); //影响的区间范围每个的ri被覆盖
if (preLast)fa[preLast] = preLast + 1; //并查集向右合并
preLast = r;
//更新到r后查询答案
for (const auto& [l,id] : child[r])
{
int L = l, R = find(l); //[l,lmax]
ans[id] = query(1, L, R);
}
}
forn(i, 1, q)write(endl, ans[i]);
} signed int main()
{
Spider
//------------------------------------------------------
int test = 1;
// read(test);
// cin >> test;
forn(i, 1, test)solve();
// while (cin >> n, n)solve();
// while (cin >> test)solve();
}
\[最终时间复杂度基于线段树和并查集的修改与查询综合考虑为\ O((n+q)\log{n})
\]

P7907 [Ynoi2005] rmscne题解的更多相关文章

  1. 2016 华南师大ACM校赛 SCNUCPC 非官方题解

    我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...

  2. noip2016十连测题解

    以下代码为了阅读方便,省去以下头文件: #include <iostream> #include <stdio.h> #include <math.h> #incl ...

  3. BZOJ-2561-最小生成树 题解(最小割)

    2561: 最小生成树(题解) Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1628  Solved: 786 传送门:http://www.lyd ...

  4. Codeforces Round #353 (Div. 2) ABCDE 题解 python

    Problems     # Name     A Infinite Sequence standard input/output 1 s, 256 MB    x3509 B Restoring P ...

  5. 哈尔滨理工大学ACM全国邀请赛(网络同步赛)题解

    题目链接 提交连接:http://acm-software.hrbust.edu.cn/problemset.php?page=5 1470-1482 只做出来四道比较水的题目,还需要加强中等题的训练 ...

  6. 2016ACM青岛区域赛题解

    A.Relic Discovery_hdu5982 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Jav ...

  7. poj1399 hoj1037 Direct Visibility 题解 (宽搜)

    http://poj.org/problem?id=1399 http://acm.hit.edu.cn/hoj/problem/view?id=1037 题意: 在一个最多200*200的minec ...

  8. 网络流n题 题解

    学会了网络流,就经常闲的没事儿刷网络流--于是乎来一发题解. 1. COGS2093 花园的守护之神 题意:给定一个带权无向图,问至少删除多少条边才能使得s-t最短路的长度变长. 用Dijkstra或 ...

  9. CF100965C题解..

    求方程 \[ \begin{array}\\ \sum_{i=1}^n x_i & \equiv & a_1 \pmod{p} \\ \sum_{i=1}^n x_i^2 & ...

  10. JSOI2016R3 瞎BB题解

    题意请看absi大爷的blog http://absi2011.is-programmer.com/posts/200920.html http://absi2011.is-programmer.co ...

随机推荐

  1. 绿色数治开采工艺: 3D 可视化智慧矿山

    前言 2021 年 2 月底,国家矿山安监局综合司发布的<"十四五"矿山安全生产规划(征求意见稿)>中再次强调要"实时采集矿山安全监控.人员位置监测.视频监控 ...

  2. 基于html5+javascript技术开发的房贷利率计算器,买房的码农们戳进来

    房贷计算器是一款专为购房者设计的实用工具应用,其主要功能是帮助用户详细计算房贷的还款金额.利息以及还款计划等.通过这款软件,用户可以更加便捷地了解到自己的还款情况和计划,从而更好地规划自己的财务.下面 ...

  3. Spring Cloud 系列:Seata 中TCC模式具体实现

    概述 https://seata.io/zh-cn/docs/dev/mode/tcc-mode https://seata.io/zh-cn/docs/user/mode/tcc TCC模式与AT模 ...

  4. 260. 只出现一次的数字 III

    1.题目介绍 2.题解 2.1 快排+遍历 思路 同本系列前几题一样 代码 class Solution { public: std::vector<int> singleNumber(s ...

  5. Oracle实例的启动和关闭

    启动模式 1.NoMount 模式(启动实例不加载数据库) 命令:startup nomount 讲解:这种启动模式只会创建实例,并不加载数据库,Oracle仅为实例创建各种内存结构和服务进程,不会打 ...

  6. 【Tomcat 组成与工作原理】

    Tomcat组成与工作原理 Tomcat 是什么 开源的 Java Web 应用服务器,实现了 Java EE(Java Platform Enterprise Edition)的部 分技术规范,比如 ...

  7. [转帖]MySQL: Convert decimal to binary

    Last Update:2018-12-05 Source: Internet  Author: User Tags decimal to binary mysql code Developer on ...

  8. [转帖]RocksDB 简介

    https://docs.pingcap.com/zh/tidb/stable/rocksdb-overview RocksDB 是由 Facebook 基于 LevelDB 开发的一款提供键值存储与 ...

  9. [转帖]Chrome 109发布,最后一个支持Windows 7/8的版本

    https://www.163.com/dy/article/HQR3QQFD0511CUMI.html 出品 | OSC开源社区(ID:oschina2013) Google 在去年 12 月 1 ...

  10. grafana与K8S的下载地址

    https://grafana.com/grafana/download/9.1.6 https://dl.k8s.io/v1.15.1/kubernetes-node-linux-amd64.tar ...