D
题意
给定长度为n的序列a,需要你寻找一个最长子序列b,满足bi ^ (i+1) < bi+1 ^ i
输出b的最大长度
思路
首先将给的那个异或条件变形,但是异或性质太少了,实在是没办法直接变形,
但是根据直觉,如果是等于号 我们可以化简为 bi ^ i == b i+1 ^ (i + 1)(p式)
我们考虑二进制形式前k位等式成立,而k+1位使得bi ^ (i+1) < bi+1 ^ i
即 在第k+1位上 bi+1 ^ i = 1,而bi ^ (i+1) = 0
通过枚举发现,这时候k+1位 bi ^ i != bi+1 ^ (i+1) ,我们找到了一个性质,当原不等式为等号时,p式成立,当枚举二进制位第一个不成立式,p式也不成立
所以我们构建01Trie字典树来查询小于bi ^ i 的每一位数来实现最长上升子序列dp的状态转移,因为是异或,有bi本身第k位0 或 1的对应,我们还要存储Trie每个节点的位置是0或1的最长上升子序列个数
代码实现
const int N = 32 * (3e5 + 10), M = 3e5 + 10;
int tr[N][2], f[N][2];
int n, dp[M], a[M];
int cnt;
void insert (int x, int idx)
{
int u = 0;
for (int i = 30 ; i >= 0; i --)
{
int p = (x >> i) & 1;
if (!tr[u][p]) tr[u][p] = ++ cnt;
u = tr[u][p];
f[u][ (idx >> i) & 1] = max (f[u][ (idx >> i) & 1], dp[idx]);
}
}
int query (int x, int k)
{
int res = 1;
int u = 0;
for (int i = 30 ; i >= 0 ; i --)
{
int p = (x >> i) & 1;
// q 是 b[i] ^ 1 的节点编号
int q = tr[u][p ^ 1];
// 根据第k位 b[i] ^ 1来更新res
res = max (res, f[q][ (k >> i) & 1 ^ 1] + 1);
if (!tr[u][p]) break;
u = tr[u][p];
}
return res;
}
void solve()
{
cin >> n;
// 初始化
for(int i = 0; i <= cnt ; i ++) tr[i][0] = tr[i][1] = f[i][0] = f[i][1] = 0;
cnt = 0;
for (int i = 0 ; i < n ; i ++)
cin >> a[i];
for (int i = 0; i < n ; i ++)
{
dp[i] = query (a[i] ^ i, a[i]);
insert (a[i] ^ i, i);
}
int res = 0;
for (int i = 0; i < n ; i ++)
res = max (res, dp[i]);
cout << res << endl;
}