P7907 [Ynoi2005] rmscne题解
题目链接:rmscne
神仙经典数据结构难题。看到求区间种类数有关的东西,需要下意识的反应到经典老题 HH的项链,这里可以学习我这篇 题解。具体学习下扫描线怎么做这类东西的。
看看本题,首先处理区间查询问题,而且是这种很复杂的子区间问题。这里的
我们固定
当
核心点的提出
那么我们反过来考虑固定的是
我们观察若干个
重新阐述下,首先呢答案
如图所示,假如
至此,大体已经讲清,正式阐述细节和算法核心思路。
算法逻辑与框架
-
首先若干个区间查询很麻烦,我们能不能只抽象成单点移动,显然参照 HH 的项链,我们只需要离线扫描线查询,从
更新 再查询对应时刻答案即可。 -
对于
的移动,基于第一点,我们会发现它所更新的 也会向右移动一位,换句话来讲这可以看做区间合并的过程,你想到了什么?经典的并查集合加快合并区间的套路。为每个点维护一个当前能快速移动到的位置,这个位置是什么东西呢?让我们来想一下,点 能移动到 处在此时 时刻。那么显而易见的是在更新 的过程中在 中,它都更新过 ,最新的 处 还未被后面覆盖,如果我们在往右移动,岂不是没有补的了?wow 这个就是我们要找的那个最大的 。 -
考虑第二点说的,当
移动,我们需要把一系列点的 改为当前的 ,显而易见的是需要区间覆盖,然后我们还需要查询基于上述的 范围内所有 后缀对应的答案前缀,也即是 ,这玩意不就是区间最小值查询吗?所以线段树维护即可,具体的维护区间最小值,区间覆盖,它们的右端点都是需要覆盖成 ,所以考虑左端点最大就行,每次覆盖更新的时候容易区间最小值一定为区间最右边的点到当前的 处,如图所示。
显然最小的区间最右边的点到当前点。然后注意区间合并并查集是向右合并,至此,全部完毕。数据量
参照代码
#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(); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!