题解:P17725 [SCP-S 2026] 大雨 / rain
先转化题意为:将序列分成若干段,每一段异或一个数,要求单调不降,求最小段数。其中可以有一个后缀不异或,不计入段数。
先不管那个后缀,假设得到了分段方法,考虑如何满足单调不降。对于相邻两段之间,注意到
对于段内,限制即
那么如果不管最后的后缀,直接预处理然后
然后考虑可能不用最后一次,即要求是否存在
此时之前的段之间不能不管了,那么考虑求
转移即
优化一下转移。第一个限制好办。第二个限制扫描到
那么考虑在
一操作直接给子树打标记,记录加入时间,复杂度
然后
#include <bits/stdc++.h>
using namespace std;
namespace Fastio {
const int L = 1 << 20;
inline int gc() {
static char buf[L], *l = buf, *r = buf;
if(l == r)
r = (l = buf) + fread(buf, 1, L, stdin);
return l == r ? EOF : *l++;
}
char obuf[L], *p = obuf;
inline void flush() {
fwrite(obuf, 1, p - obuf, stdout), p = obuf;
}
inline void pc(char c) {
if(p == obuf + L)
flush();
*p++ = c;
}
struct Reader {
template <typename T>
inline Reader& operator>>(T &x) {
x = 0;
int c = gc(), f = 1;
while(c < '0' || c > '9') {
if(c == '-')
f = -1;
c = gc();
}
while(c >= '0' && c <= '9')
x = x * 10 + c - '0', c = gc();
x *= f;
return *this;
}
}cin;
struct Writer {
template <typename T>
inline Writer& operator<<(T x) {
if(x < 0)
pc('-'), x = -x;
char s[24], *t = s + 24;
while(x >= 100) {
int lim = x % 100;
x /= 100;
*--t = lim % 10 + '0', *--t = lim / 10 + '0';
}
if(x < 10)
*--t = x + '0';
else
*--t = x % 10 + '0', *--t = x / 10 + '0';
int len = s + 24 - t;
if(p + len > obuf + L)
flush();
memcpy(p, t, len), p += len;
return *this;
}
inline Writer& operator<<(char c) {
pc(c);
return *this;
}
inline Writer& operator<<(const char *s) {
while(*s)
pc(*s++);
return *this;
}
~Writer() {
flush();
}
}cout;
#define cin Fastio::cin
#define cout Fastio::cout
}
const int inf = 1 << 30;
int c, t, n, ans, a[1000005], f[1000005], lim[30];
struct Trie {
int cnt, head, tot, rt[2], d[30][2], pre[30], to[32000005][2], tag[32000005];
int build() {
int x;
if(head)
x = head, head = to[x][0];
else
x = ++cnt;
return to[x][0] = to[x][1] = tag[x] = 0, x;
}
void clear(int x) {
if(!x)
return;
clear(to[x][0]), clear(to[x][1]), to[x][0] = head, head = x;
return;
}
void addtag(int &x, int t) {
if(!x)
x = build();
else
clear(to[x][0]), clear(to[x][1]);
to[x][0] = to[x][1] = 0, tag[x] = t;
return;
}
void pushdown(int x) {
if(tag[x])
addtag(to[x][0], tag[x]), addtag(to[x][1], tag[x]), tag[x] = 0;
return;
}
void insert(int &x, int v, int b, int k, int t) {
if(!x)
x = build();
if(k < 0 || !(b & (1 << k + 1) - 1))
return addtag(x, t);
pushdown(x);
int p = v >> k & 1, q = b >> k & 1;
if(!q)
addtag(to[x][p ^ 1], t);
return insert(to[x][p ^ q], v, b, k - 1, t);
}
inline void del(int k, int x) {
d[k][x] = ++tot;
for(int i = k; i < 30; i++)
pre[i] = max(pre[i], min(d[k][0], d[k][1]));
return;
}
int query(int &x, int v, int k, int mx) {
if(!x)
return inf;
if(tag[x]) {
if(tag[x] <= mx || ~k && tag[x] <= pre[k])
return clear(x), x = 0, inf;
int s = 0;
for(int i = k; ~i; i--)
if(d[i][v >> i & 1] >= tag[x])
s |= 1 << i;
return s;
}
if(!~k)
return clear(x), x = 0, inf;
int p = v >> k & 1, s = query(to[x][p], v, k - 1, max(mx, d[k][p]));
if(s == inf && (s = query(to[x][p ^ 1], v, k - 1, max(mx, d[k][p ^ 1]))) < inf)
s |= 1 << k;
if(s == inf && !to[x][0] && !to[x][1])
clear(x), x = 0;
return s;
}
void init() {
memset(d, 0, sizeof(d)), memset(pre, rt[0] = rt[1] = cnt = head = tot = 0, sizeof(pre));
return;
}
}T;
int main() {
cin >> c >> t;
while(t--) {
cin >> n, fill(lim, lim + 30, -1), f[0] = 0, f[1] = 1;
for(int i = 1; i <= n; i++)
cin >> a[i];
int last = 0;
for(int i = 2; i <= n; i++) {
f[i] = f[i - 1];
if(a[i - 1] > a[i])
last = i - 1;
if(a[i - 1] ^ a[i]) {
int k = __lg(a[i - 1] ^ a[i]);
if(~lim[k] && lim[k] != (a[i - 1] >> k & 1))
f[i]++, fill(lim, lim + 30, -1);
else
lim[k] = a[i - 1] >> k & 1;
}
}
if(f[n] == 1) {
cout << !!last << '\n';
continue;
}
T.init(), ans = f[n];
for(int i = 1, g = 0; i <= n; i++) {
if(i > 1 && a[i - 1] != a[i]) {
int k = __lg(a[i - 1] ^ a[i]);
T.del(k, (a[i - 1] >> k & 1) ^ 1);
}
if(g < inf)
T.insert(T.rt[1], a[i], g, 29, ++T.tot);
if(i < 2 || f[i] > f[i - 1])
T.clear(T.rt[0]), T.rt[0] = T.rt[1], T.rt[1] = 0;
g = T.query(T.rt[0], a[i], 29, 0);
if(i >= last && i < n && f[i] == f[n] - 1 && g <= a[i + 1])
ans = f[n] - 1;
}
cout << ans << '\n', T.clear(T.rt[0]), T.clear(T.rt[1]);
}
return 0;
}