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

题意

\(n\) 个区间,第 \(i\) 个区间是 \([l_i,r_i]\) ,它的长度是 \(r_i-l_i\)

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

\(n,q≤500,000\)

题解

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

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

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

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

假设我们当前算出了对于一个询问 所有长度 \(\le k\) 的区间个数 \(res_k\) , 那这个答案就可以表示成 \(res_n - res_{K-1}\) .

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

这个如何做呢 qwq

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

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

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

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

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

不难发现长度会有限制 , 那我们询问的时候 只要询问所有 \(len \le R - L\) 的区间就行了 , 也就是每个离线后变成计算 \(res_{R-L} - res_{K - 1}\) .

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

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

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

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

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

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

注意一开始离线的时候 要特判掉 \(R-L < 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;
}
posted @ 2018-06-15 21:18  zjp_shadow  阅读(423)  评论(0编辑  收藏  举报