LOJ #6270. 数据结构板子题 (离线+树状数组)

题意

n 个区间,第 i 个区间是 [li,ri] ,它的长度是 rili

q 个询问,每个询问给定 L,R,K ,询问被 [L,R] 包含的且长度不小于 K 的区间数量。

n,q500,000

题解

想了无数种 O((n+q)log2n) 的做法啊TAT 后来看了 这份代码 后恍然大悟 .

这题一个很显然的想法是离线 qwq

首先离线 l or r 似乎不太可行 , 因为要动态支持查找一个区间 [L,R] 不小于 K 的个数 , 而主席树需要离线完成 (划分树没学过 , 不知道可不可以) 在线的话只有 O(log2) 的复杂度可行了 .

那继续考虑离线 K , 然后这样的话 , 我们就可以忽略 K 的限制 .

假设我们当前算出了对于一个询问 所有长度 k 的区间个数 resk , 那这个答案就可以表示成 resnresK1 .

我们此时只需要做的就是 计算一个区间包含了当前的多少个区间 .

这个如何做呢 qwq

两个区间 A,B 只有三种情况 .

  1. AB (此时 A 可以等于 B ) , B 完全包含 A , |B||A|
  2. ABBA , 此时 A,B 交的部分不会是这两个区间中任意一个全集 .
  3. BA (AB) , A 完全包含 B , |A|>|B|

我们假设前面插入的区间为 A , 当前询问的区间为 B . 我们计算的就只有第 1 种情况了 .

不难发现 第 3 情况计数很麻烦 .

我们最好忽略第 3 种情况 , 只计算第 2 种情况 . (因为这个 A 会有两个出去不好计算)

不难发现长度会有限制 , 那我们询问的时候 只要询问所有 lenRL 的区间就行了 , 也就是每个离线后变成计算 resRLresK1 .

而对于第 2 种情况 , A 只有一端会超出 B .

然后每次询问 resi 的时候 假设当前插入的线段的总数是 tot .

答案显然就是 tot 前面左端点存在于 [1,L1] 的线段个数 - 前面右端点存在于 [R+1,n] 的线段个数 .

(可以发现 , 这样很好地处理了两个线段相离的情况)

然后这个用两个树状数组统计一下 , 左端点和右端点各一个 .

所以最后的时间复杂度就是 O((n+q)logn) 了 .

注意一开始离线的时候 要特判掉 RL<K 的情况 !!

最好自己画图理解 , 博主懒就没放上来了qwq

代码

#include <bits/stdc++.h> #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Set(a, v) memset(a, v, sizeof(a)) #define debug(x) cout << #x << ':' << x << endl using namespace std; inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;} inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;} inline int read() { int x = 0, fh = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1; for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48); return x * fh; } void File() { #ifdef zjp_shadow freopen ("6270.in", "r", stdin); freopen ("6270.out", "w", stdout); #endif } const int N = 500100; int n, q; #define lowbit(x) (x & -x) struct Fenwick_Tree { int sumv[N]; inline void Update(int pos) { for(; pos <= n; pos += lowbit(pos)) ++ sumv[pos]; } inline int Query(int pos) { int res = 0; for (; pos > 0; pos ^= lowbit(pos)) res += sumv[pos]; return res; } } Pre, Suf; struct Ask { int opt, l, r, id; } ; typedef pair<int, int> PII; #define fir first #define sec second vector<Ask> Q[N]; vector<PII> V[N]; int ans[N]; #define Rev(x) (n - (x) + 1) int main () { File(); n = read(); q = read(); For (i, 1, n) { int l = read(), r = read(), len = r - l; V[len].push_back(make_pair(l, r)); } For (i, 1, q) { int l = read(), r = read(), k = read(), len = r - l; if (len >= k) Q[k - 1].push_back((Ask) {-1, l, r, i}), Q[len].push_back((Ask) {1, l, r, i}); } int tot = 0; For (i, 1, n) { for (PII Up : V[i]) Pre.Update(Up.fir), Suf.Update(Rev(Up.sec)), ++ tot; for (Ask Rp : Q[i]) ans[Rp.id] += Rp.opt * (tot - Pre.Query(Rp.l - 1) - Suf.Query(Rev(Rp.r + 1))); } For (i, 1, q) printf ("%d\n", ans[i]); return 0; }

__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/9189014.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(425)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示