[NOI2007] 项链工厂 题解

前言

题目链接:洛谷Hydro & bzoj

题意简述

yzh 喜欢写 DS 题!你要维护一个环:

  1. 顺时针移动 \(k\) 位;
  2. 翻转 \(2 \sim n\)
  3. 交换 \(i\)\(j\)
  4. 区间覆盖;
  5. 查询整个环有几个颜色段;
  6. 查询 \(i \sim j\) 有几个颜色段。

题目分析

平衡树板子啊,代码很好写,\(273\) 行。但是为什么不使用线段树呢?

发现,顺时针移位,原本该连续的区间也还在一块(这里指的是环上,在序列上可能一个在首一个在尾,但这对分析问题并不重要)。所以遇到移位操作,只用记录一个偏移量,查询的时候对下表进行相应处理即可。同理,翻转操作也不必真的进行翻转,只用记一个翻转标记,每次让它异或 \(1\) 即可。我们可以轻松写出如下转换函数。

auto trans = [&mov, &flip] (int x) -> int {
	if (flip) x = mov - x + 2;
	else x = x - mov;
	x = (x % n + n) % n;
	return x ? x : n;
};

接下来考虑线段树如何实现。我们发现,问题变成了区间覆盖、单点查询颜色、区间查询颜色段数。很套路,在信息中记录左右端点的颜色以及区间内的颜色段数。合并的时候把两个自区间颜色段数相加,如果左边的右端点和右边的左端点颜色相同,再减去一次重复算的这一次。

struct Info {
	int l, r, cnt;
	friend Info operator + (const Info& a, const Info& b) {
		return { a.l, b.r, a.cnt + b.cnt - (a.r == b.l) };
	}
};

剩下的板子不展开。

代码

// #pragma GCC optimize(3)
// #pragma GCC optimize("Ofast", "inline", "-ffast-math")
// #pragma GCC target("avx", "sse2", "sse3", "sse4", "mmx")
#include <iostream>
#include <cstdio>
#define debug(a) cerr << "Line: " << __LINE__ << " " << #a << endl
#define print(a) cerr << #a << "=" << (a) << endl
#define file(a) freopen(#a".in", "r", stdin), freopen(#a".out", "w", stdout)
#define main Main(); signed main() { return ios::sync_with_stdio(0), cin.tie(0), Main(); } signed Main
using namespace std;

int n, m, col[500010];

struct Segment_Tree {
	#define lson (idx << 1    )
	#define rson (idx << 1 | 1)
	
	struct Info {
		int l, r, cnt;
		friend Info operator + (const Info& a, const Info& b) {
			if (a.l == -1) return b;
			if (b.l == -1) return a;
			return { a.l, b.r, a.cnt + b.cnt - (a.r == b.l) };
		}
	};
	
	struct node {
		int l, r;
		int tag;
		Info info;
	} tree[500010 << 2];
	
	void build(int idx, int l, int r) {
		tree[idx] = {l, r, -1, {0, 0, 0}};
		if (l == r) return tree[idx].info = {col[l], col[r], 1}, void();
		int mid = (l + r) >> 1;
		build(lson, l, mid);
		build(rson, mid + 1, r);
		tree[idx].info = tree[lson].info + tree[rson].info;
	}
	
	void pushtag(int idx, int tag) {
		tree[idx].tag = tag;
		tree[idx].info = {tag, tag, 1};
	}
	
	void pushdown(int idx) {
		if (tree[idx].tag == -1) return;
		pushtag(lson, tree[idx].tag);
		pushtag(rson, tree[idx].tag);
		tree[idx].tag = -1;
	}
	
	void modify(int idx, int l, int r, int tag) {
		if (tree[idx].l > r || tree[idx].r < l) return;
		if (l <= tree[idx].l && tree[idx].r <= r) return pushtag(idx, tag);
		pushdown(idx);
		modify(lson, l, r, tag);
		modify(rson, l, r, tag);
		tree[idx].info = tree[lson].info + tree[rson].info;
	}
	
	Info query(int idx, int l, int r) {
		if (tree[idx].l > r || tree[idx].r < l) return {-1, -1, 0};
		if (l <= tree[idx].l && tree[idx].r <= r) return tree[idx].info;
		pushdown(idx);
		return query(lson, l, r) + query(rson, l, r);
	}
	
	#undef lson
	#undef rson
} yzh;

#ifdef XuYueming
#define printf printf(">>> "), printf
#endif

signed main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i)
		scanf("%d", &col[i]);
	scanf("%d", &m), yzh.build(1, 1, n);
	for (int i, j, c, k, mov = 0, flip = 0; m--; ) {
		static char op[10];
		static auto trans = [&mov, &flip] (int x) -> int {
			if (flip) x = mov - x + 2;
			else x = x - mov;
			x = (x % n + n) % n;
			return x ? x : n;
		};
		scanf("%s", op);
		if (*op == 'R') {
			scanf("%d", &k);
			mov = (mov + k) % n;
		} else if (*op == 'F') {
			flip ^= 1, mov = (n - mov) % n;
		} else if (*op == 'S') {
            scanf("%d%d", &i, &j);
			i = trans(i), j = trans(j);
			int ci = yzh.query(1, i, i).l, cj = yzh.query(1, j, j).l;
			yzh.modify(1, i, i, cj), yzh.modify(1, j, j, ci);
        } else if (*op == 'P') {
            scanf("%d%d%d", &i, &j, &c);
			i = trans(i), j = trans(j);
			if (flip) swap(i, j);
			if (i <= j) {
				yzh.modify(1, i, j, c);
			} else {
				yzh.modify(1, i, n, c);
				yzh.modify(1, 1, j, c);
			}
        } else if (op[1] == '\0') {
			printf("%d\n", max(1, yzh.tree[1].info.cnt - (yzh.tree[1].info.l == yzh.tree[1].info.r)));
        } else {
            scanf("%d%d", &i, &j);
			i = trans(i), j = trans(j);
			if (flip) swap(i, j);
			if (i <= j) {
				printf("%d\n", yzh.query(1, i, j).cnt);
			} else {
				printf("%d\n", (yzh.query(1, i, n) + yzh.query(1, 1, j)).cnt);
			}
        }
	}
	return 0;
}

后记 & 反思

没有敏锐地发现连续段在操作后还是连续的这一性质,导致没秒掉这道水题。

posted @ 2024-07-19 14:31  XuYueming  阅读(22)  评论(0编辑  收藏  举报