NOI 2025 D2T1 三目运算符 题解
三目运算符
Description
对于一个长度为 \(n\) (\(n \geq 3\)) 的 01 串 \(S = s_1 \ldots s_n\),定义变换 \(T = f(S) = t_1 \ldots t_n\) 如下:
定义变换 \(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\))。
考虑修改,肯定要用线段树。需要查询:
-
是否存在 \(101\);
-
第一个 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;
}

浙公网安备 33010602011771号