NOI 2025 D2T1 三目运算符 题解

三目运算符

link

Description

对于一个长度为 \(n\) (\(n \geq 3\)) 的 01 串 \(S = s_1 \ldots s_n\),定义变换 \(T = f(S) = t_1 \ldots t_n\) 如下:

\[t_i = \begin{cases} s_i, & i \leq 2, \\ s_i, & i \geq 3 \text{ 且 } s_{i-2} = 0, \\ s_{i-1}, & i \geq 3 \text{ 且 } s_{i-2} = 1. \end{cases}\]

定义变换 \(f\)不动点 如下:若 01 串 \(T\) 满足 \(f(T) = T\),则称 \(T\) 为变换 \(f\) 的不动点。

\(f^k(S)\)\(S\) 经过 \(k\) 次变换得到的串。特别地,记 \(f^0(S) = S\)。求最小的自然数 \(k\),使得 \(f^k(S)\) 为变换 \(f\) 的不动点,即满足 \(f^{k+1}(S) = f^k(S)\) 的最小的自然数 \(k\)。可以证明,一定存在自然数 \(k\) 使得 \(f^k(S)\) 为变换 \(f\) 的不动点。

小 Z 觉得这个问题过于简单,因此他增加了 \(q\) 次修改操作。第 \(i\) (\(1 \leq i \leq q\)) 次修改会给定两个正整数 \(l_i, r_i\) (\(1 \leq l_i \leq r_i \leq n\)),然后将区间 \([l_i, r_i]\) 内的所有原有的 0 替换为 1,所有原有的 1 替换为 0。你需要对初始时及每次修改后的字符串 \(S\),求出最小的自然数 \(k\),使得 \(f^k(S)\) 为变换 \(f\) 的不动点。

本题包含多组测试数据。

输入的第一行包含两个非负整数 \(c, t\),分别表示测试点编号与测试数据组数。\(c = 0\) 表示该测试点为样例。

接下来依次输入每组测试数据,对于每组测试数据:

第一行包含两个正整数 \(n, q\),分别表示 \(S\) 的长度和修改次数。

第二行包含一个长度为 \(n\) 的 01 串 \(S = s_1 \ldots s_n\),表示初始时的字符串。

\(i + 2\) (\(1 \leq i \leq q\)) 行包含两个正整数 \(l_i, r_i\),表示一次修改操作。

对于每组测试数据,设初始时的答案为 \(k_0\),第 \(i\) (\(1 \leq i \leq q\)) 次修改后的答案为 \(k_i\),输出一行一个正整数,表示 \(\oplus_{i=0}^{q} ((i + 1) \times k_i)\),其中 \(\oplus\) 表示 二进制按位异或

\(N, Q\) 分别为单个测试点内所有测试数据的 \(n, q\) 的和。对于所有测试数据,保证:

  • \(1 \leq t \leq 5\);
  • \(3 \leq n \leq 4 \times 10^5\), \(N \leq 8 \times 10^5\);
  • \(1 \leq q \leq 4 \times 10^5\), \(Q \leq 8 \times 10^5\);
  • 对于所有 \(1 \leq i \leq n\), 均有 \(s_i \in \{0, 1\}\);
  • 对于所有 \(1 \leq i \leq q\), 均有 \(1 \leq l_i \leq r_i \leq n\)

Solution

性质:对于串 \(s\),它的 \(t=f(s)\) 是固定、离线、唯一的,即未完成的 \(f(s)\) 的生成不受到已生成的 \(f(s)\) 的影响。

对于 \(s_i\),它会且仅会受到 \(s_{i-1}\)\(s_{i-2}\) 的影响。考虑分类讨论:

000 -> 000
001 -> 001
010 -> 010
011 -> 011
100 -> 100
101 -> 100
110 -> 111
111 -> 111

注意到,只有 \(101\)\(110\) 会有影响,其余三位子串均是不动的。这两种串的影响方式不同,打表发现:\(101\) 经一次操作会直接变为 \(100\) 且不再改变;\(110\) 经一次操作会整个三位子串向右移动一位(即 \(110?\) -> \(?110\)),直到字符串尽头。

因此,如果不考虑修改操作,\(101\) 的贡献为 \(1\)\(110\) 的贡献为 \(n - i - 1\),其中 \(i\)\(110\) 中,第一个位置的索引。

综上:如果原串 \(s\) 中存在子串 \(110\),那么取第一个 \(110\) 的第一个 \(1\) 的索引为 \(i\),则 \(n - i + 1\) 即为答案;如果没有 \(110\) 但有 \(101\),则 \(1\) 为答案;否则,\(0\) 为答案。

例如,\(00100[110]11010100010\)\(n = 19,\ i_1 = 6,\ ans = 19 - 6 - 1\))。

考虑修改,肯定要用线段树。需要查询:

  1. 是否存在 \(101\)

  2. 第一个 110 出现的位置;

操作是区间取反。

对于 1,维护是否存在 \(101\) 和是否存在 \(010\)。对于 2,维护:可能的前后缀(\(0, 1, 00, 01, 10, 11\))是否贴边,以及 \(110\)\(001\) 出现的首次位置。

多测要清空!

Code

#include <bits/stdc++.h>
#define int long long
#define inf 1e18
#define debug cout << '!';
#define filein(x) freopen(#x".in", "r", stdin);
#define fileout(x) freopen(#x".out", "w", stdout);
#define file(x) filein(x) fileout(x)
// #define Fast_IO
using namespace std;
#ifdef Fast_IO
inline int read() {
	int x = 0, f = 1; char c = getchar();
	while (c < '0' or c > '9') { if (c == '-') f = -1; c = getchar(); }
	while (c >= '0' and c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
	return x * f;
}
void write(int x) {
	if (x < 0) putchar('-'), x = -x;
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0'); return;
}
#endif
const int N = 4e5 + 5;
int c, T, n, q, a[N], ans;
string s;
struct SegmentTree {
	#define mid ((l + r) >> 1)
	#define lson (id << 1)
	#define rson (id << 1 | 1)
	struct Node {
		int l, r;
		bool has101, has010;
		bool pre0, pre1, pre00, pre01, pre10, pre11;
		bool suf0, suf1, suf00, suf01, suf10, suf11;
		int fst110, fst001;
		bool lzy;
		Node() {
			 l = r = has101 = has010 = pre0 = pre1 = pre00 = pre01 = pre10 = pre11 = suf0 = suf1 = suf00 = suf01 = suf10 = suf11 = lzy = 0;
			fst110 = fst001 = inf;
		}
	} t[N << 2];
	void pushup(int id) {
		t[id].pre0 = t[lson].pre0;
		t[id].pre1 = t[lson].pre1;
		t[id].suf0 = t[rson].suf0;
		t[id].suf1 = t[rson].suf1;
		
		t[id].pre00 = t[id].pre01 = t[id].pre10 = t[id].pre11 = t[id].suf00 = t[id].suf01 = t[id].suf10 = t[id].suf11 = t[id].has101 = t[id].has010 = 0;
		t[id].fst110 = t[id].fst001 = inf;
		
		if (t[id].l == t[id].r) return;
		if (t[lson].l == t[lson].r) {
			t[id].pre00 = t[lson].pre0 and t[rson].pre0;
			t[id].pre01 = t[lson].pre0 and t[rson].pre1;
			t[id].pre10 = t[lson].pre1 and t[rson].pre0;
			t[id].pre11 = t[lson].pre1 and t[rson].pre1;
		} else {
			t[id].pre00 = t[lson].pre00;
			t[id].pre01 = t[lson].pre01;
			t[id].pre10 = t[lson].pre10;
			t[id].pre11 = t[lson].pre11;
		}
		if (t[rson].l == t[rson].r) {
			t[id].suf00 = t[lson].suf0 and t[rson].suf0;
			t[id].suf01 = t[lson].suf0 and t[rson].suf1;
			t[id].suf10 = t[lson].suf1 and t[rson].suf0;
			t[id].suf11 = t[lson].suf1 and t[rson].suf1;
		} else {
			t[id].suf00 = t[rson].suf00;
			t[id].suf01 = t[rson].suf01;
			t[id].suf10 = t[rson].suf10;
			t[id].suf11 = t[rson].suf11;
		}
		
		if (t[id].r - t[id].l + 1 <= 2) return;
		t[id].has101 = t[lson].has101 or t[rson].has101;
		t[id].has010 = t[lson].has010 or t[rson].has010;
		t[id].has101 |= (t[lson].suf1 and t[rson].pre01) or (t[lson].suf10 and t[rson].pre1);
		t[id].has010 |= (t[lson].suf0 and t[rson].pre10) or (t[lson].suf01 and t[rson].pre0);
		
		t[id].fst110 = min(t[lson].fst110, t[rson].fst110);
		t[id].fst001 = min(t[lson].fst001, t[rson].fst001);
		if (t[lson].suf1 and t[rson].pre10) t[id].fst110 = min(t[id].fst110, t[lson].r);
		if (t[lson].suf0 and t[rson].pre01) t[id].fst001 = min(t[id].fst001, t[lson].r);
		if (t[lson].suf11 and t[rson].pre0) t[id].fst110 = min(t[id].fst110, t[lson].r - 1);
		if (t[lson].suf00 and t[rson].pre1) t[id].fst001 = min(t[id].fst001, t[lson].r - 1);
	}
	
	void setlzy(int id, bool lzy) {
		if (not lzy) return;
		swap(t[id].pre0, t[id].pre1);
		swap(t[id].suf0, t[id].suf1);
		swap(t[id].pre00, t[id].pre11);
		swap(t[id].suf00, t[id].suf11);
		swap(t[id].pre01, t[id].pre10);
		swap(t[id].suf01, t[id].suf10);
		swap(t[id].has101, t[id].has010);
		swap(t[id].fst110, t[id].fst001);
		t[id].lzy ^= 1;
	}
	void pushdown(int id) {
		if (t[id].lzy) {
			setlzy(lson, t[id].lzy);
			setlzy(rson, t[id].lzy);
			t[id].lzy = 0;
		}
	}
	void build(int id, int l, int r) {
		t[id] = Node();
		t[id].l = l, t[id].r = r;
		if (l == r) {
			if (a[l]) t[id].pre1 = t[id].suf1 = 1;
			else t[id].pre0 = t[id].suf0 = 1;
		} else {
			build(lson, l, mid);
			build(rson, mid + 1, r);
			pushup(id);
		}
	}
	void modify(int id, int l, int r, int ql, int qr, bool lzy) {
		if (ql == l and qr == r) {
			setlzy(id, lzy);
			return;
		} else {
			pushdown(id);
			if (qr <= mid) modify(lson, l, mid, ql, qr, lzy);
			else if (ql > mid) modify(rson, mid + 1, r, ql, qr, lzy);
			else {
				modify(lson, l, mid, ql, mid, lzy);
				modify(rson, mid + 1, r, mid + 1, qr, lzy);
			}
			pushup(id);
		}
	}
	int query() {
		if (t[1].fst110 != inf) return n - t[1].fst110 - 1;
		else if (t[1].has101) return 1;
		else return 0;
	}
	#undef mid
	#undef lson
	#undef rson
} seg;
void solve() {
	memset(a, 0, sizeof(a));
	ans = 0;
	cin >> n >> q >> s;
	for (int i = 1; i <= n; i++) {
		a[i] = s[i - 1] - '0';
	}
	seg.build(1, 1, n);
	ans = seg.query();
	// cout << seg.query() << '\n';
	for (int i = 1; i <= q; i++) {
		int l, r; cin >> l >> r;
		seg.modify(1, 1, n, l, r, 1);
		// cout << seg.query() << '\n';
		ans ^= (i + 1) * seg.query();
	}
	cout << ans << '\n';
}
signed main() {
	cin.tie(0) -> sync_with_stdio(0);
	cin >> c >> T;
	while (T--) {
		solve();
	}
	return 0;
}
posted @ 2026-04-25 11:09  L-Coding  阅读(8)  评论(0)    收藏  举报