HDU4638 Group (莫队 / 询问离线+线段树)

题目链接: Group

大致题意

有一个长度为 n n n 1 1 1~ n n n排列, 保证序列中没有相同的元素. 有 m m m次询问, 每次询问 [ l , r ] [l, r] [l,r]中所有的元素可以分成多少个不相交的线段.

解题思路

解法一: 莫队

我们考虑用莫队去维护 [ l , r ] [l, r] [l,r]的信息. 设当前位置为 p o s pos pos, 元素值为 v a l val val. 如果 v a l + 1 val + 1 val+1 v a l − 1 val - 1 val1均出现过, 则答案 − 1 -1 1. 否则, 若二者均未出现, 则答案 + 1 +1 +1. 其他情况答案不变.

因此我们可以通过一个来维护各个元素的出现情况.


解法二: 线段树

考虑到线段树不像莫队, 每次保证维护的信息只有查询区间 [ l , r ] [l, r] [l,r]的信息那样的优秀性质. 我们只能考虑每个位置对于答案的贡献.

设当前位置为 p o s pos pos, 元素值为 v a l val val. 如果此时 v a l − 1 val - 1 val1出现过, 那么由于 v a l val val的出现, 因此 p o s v a l − 1 pos_{val - 1} posval1处的贡献 − 1 -1 1. 对于 v a l + 1 val + 1 val+1的情况同理.

但是每次询问的区间是任意的, 我们修改 p o s v a l ± 1 pos_{val \pm 1} posval±1处的贡献的前提是 v a l val val一定出现了, 但可能前一次的查询区间 [ l ′ , r ′ ] [l', r'] [l,r]包含了 p o s pos pos位置, 这次的 [ l , r ] [l, r] [l,r]就不包含 p o s pos pos位置了, 导致我们统计出了错误的答案.


我们考虑对于询问按照右端点进行排序. 这样当我们加入 p o s pos pos位置时, 如果出现过 v a l ± 1 val \pm 1 val±1, 我们再对 p o s v a l ± 1 pos_{val\pm1} posval±1进行修改. 此时如果询问区间包含了, p o s v a l ± 1 pos_{val\pm1} posval±1那么一定会包含位置 p o s pos pos.

因为我们已经按照询问区间的右端点排序了, 如果修改了 p o s v a l ± 1 pos_{val\pm1} posval±1, 表明 p o s v a l ± 1 pos_{val\pm1} posval±1 p o s pos pos之前出现过, 因此上述说法成立.

如果 l < p o s v a l ± 1 l < pos_{val\pm1} l<posval±1呢? 如果我们的查询区间已经不包含 p o s v a l ± 1 pos_{val\pm1} posval±1, 那么修改这个位置的值也不会影响答案的正确性.


综上所述, 我们可以通过离线询问 + 线段树的方式解题.

AC代码

莫队

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E5 + 10, B = 320;
int w[N];
struct mo {
    
    
	int l, r, id;
	bool operator< (const mo& t) const {
    
    
		if (l / B != t.l / B) return l < t.l;
		return l / B & 1 ? r < t.r : r > t.r;
	}
}; vector<mo> area;


int res[N];
bool vis[N]; int qsum;
void add(int c) {
    
    
	vis[c] = 1;
	if (vis[c - 1] and vis[c + 1]) qsum--;
	else if (!vis[c - 1] and !vis[c + 1]) qsum++;
}
void sub(int c) {
    
    
	vis[c] = 0;
	if (vis[c - 1] and vis[c + 1]) qsum++;
	else if (!vis[c - 1] and !vis[c + 1]) qsum--;
}
int main()
{
    
    
	int T; cin >> T;
	while (T--) {
    
    
		int n, m; scanf("%d %d", &n, &m);
		rep(i, n) scanf("%d", &w[i]);
		rep(i, m) {
    
    
			int l, r; scanf("%d %d", &l, &r);
			area.push_back({
    
     l, r, i });
		}
		sort(area.begin(), area.end());

		int L = 1, R = 0;
		for (auto& op : area) {
    
    
			int l = op.l, r = op.r, id = op.id;
			while (l < L) add(w[--L]);
			while (r > R) add(w[++R]);
			while (L < l) sub(w[L++]);
			while (R > r) sub(w[R--]);

			res[id] = qsum;
		}

		rep(i, m) printf("%d\n", res[i]);

		while (L <= R) sub(w[L++]); // 清空
		area.clear();
	}
    
    return 0;
}

线段树

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E5 + 10;
int w[N];
struct node {
    
    
	int l, r;
	int val;
}t[N << 2];
void pushup(int x) {
    
     t[x].val = t[x << 1].val + t[x << 1 | 1].val; }
void build(int l, int r, int x = 1) {
    
    
	t[x] = {
    
     l, r, r - l + 1 };
	if (l == r) return;
	int mid = l + r >> 1;
	build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1);
}

void modify(int a, int c, int x = 1) {
    
    
	if (t[x].l == t[x].r) {
    
    
		t[x].val += c;
		return;
	}
	int mid = t[x].l + t[x].r >> 1;
	modify(a, c, x << 1 | (a > mid));
	pushup(x);
}

int ask(int l, int r, int x = 1) {
    
    
	if (l <= t[x].l and r >= t[x].r) return t[x].val;
	int mid = t[x].l + t[x].r >> 1;
	int res = 0;
	if (l <= mid) res = ask(l, r, x << 1);
	if (r > mid) res += ask(l, r, x << 1 | 1);
	return res;
}

struct query {
    
    
	int l, r, id;
	bool operator< (const query& t) const {
    
     return r < t.r; }
}area[N];
int res[N];
int vis[N];
int main()
{
    
    
	int T; cin >> T;
	while (T--) {
    
    
		int n, m; scanf("%d %d", &n, &m);
		rep(i, n) scanf("%d", &w[i]), vis[i] = 0;
		build(1, n);

		rep(i, m) {
    
    
			int l, r; scanf("%d %d", &l, &r);
			area[i] = {
    
     l, r, i };
		}
		sort(area + 1, area + 1 + m);

		int pos = 0;
		rep(i, m) {
    
    
			int l = area[i].l, r = area[i].r, id = area[i].id;
			while (pos + 1 <= r) {
    
    
				pos++;
				int val = w[pos]; vis[val] = pos;
				if (val - 1 >= 1 and vis[val - 1]) {
    
    
					modify(vis[val - 1], -1);
				}
				if (val + 1 <= n and vis[val + 1]) {
    
    
					modify(vis[val + 1], -1);
				}
			}
			res[id] = ask(l, r);
		}

		rep(i, m) printf("%d\n", res[i]);
	}

    return 0;
}

END

猜你喜欢

转载自blog.csdn.net/weixin_45799835/article/details/121370226