前言
感觉 D1 和 D2 不是同一个难度档次的呀……
思路
设 /(a_j/oplus i < a_i /oplus j/),这意味着数字 /(a_j/oplus i/) 中,从个位起前 /(k/) 位和 /(a_i /oplus j/) 相同,之后第 /(k+1/) 位就不同了。
两个不同下标的数有点难处理,考虑转化为同一个下标的数之间异或(例如 /(a_i /oplus i/))。
发现,如果 /(a_j/oplus i/) 和 /(a_i/oplus j/) 中的第 /(k/) 位相同,那么这些位在 /(a_j /oplus j/) 和 /(a_i /oplus i/) 也是相同的。
异或容易联想到 01 trie。考虑对 /(a_i /oplus i/) 拆位,每一位都添加到 trie 里。
可以先计算这个数的贡献值,然后在 trie 中一边插入,一边用这个贡献值更新答案。
发现 /(x /oplus y = 1/),肯定是 /(x = 0, y = 1/) 或者 /(x = 1, y = 0/)。
所以,这一位有贡献,当且仅当两个数位的值不同。因此这时,我们就在 trie 的另一边计算贡献。
差不多就这样了。需要注意,应该先计算贡献,再插入。
完整代码
#include <iostream>
#include <cstdio>
#define space putchar(' ')
#define endl putchar('/n')
using namespace std;
typedef long long LL;
typedef long double LD;
void fastio()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
}
LL read()
{
char op = getchar(); LL x = 0, f = 1;
while (op < 48 || op > 57) {if (op == '-') f = -1; op = getchar();}
while (48 <= op && op <= 57) x = (x << 1) + (x << 3) + (op ^ 48), op = getchar();
return x * f;
}
void write(LL x)
{
if (x < 0) putchar('-'), x = -x;
if (x > 9) write(x / 10);
putchar(x % 10 + 48);
}
const int N = 3e5 + 5;
int a[N], n;
struct Trie
{
int ch[N * 31][2], dp[N * 31][2], cur;
void init() //多测不清空,爆零两行泪!
{
for (int i = 0; i <= cur; i++)
for (int j = 0; j <= 1; j++)
ch[i][j] = dp[i][j] = 0;
cur = 0;
}
#define bit(x) (bool)(x & (1 << j)) //求x二进制下的第j位
#define upd(x, y) x = max(x, y) //x = max(x, y)
int getDP(int i) //i表示下标
{
int x = a[i] ^ i, pos = 0;
int maxn = 0; //用于更新dp
for (int j = 30; j >= 0; j--)
{
//printf("bit = %d./n", bit(x));
if (ch[pos][!bit(x)]) upd(maxn, dp[ch[pos][!bit(x)]][!bit(a[i])]); //数位不同,异或才有贡献
if (ch[pos][bit(x)]) pos = ch[pos][bit(x)];
else break; //有就继续往下upd maxn,没有就停止
}
return maxn;
}
void insert(int i, int maxn) //使用maxn更新字典树与dp
{
int x = a[i] ^ i, pos = 0;
for (int j = 30; j >= 0; j--)
{
if (!ch[pos][bit(x)]) ch[pos][bit(x)] = ++cur; //字典树基本操作
upd(dp[ch[pos][bit(x)]][bit(i)], maxn);
pos = ch[pos][bit(x)];
}
}
}trie;
void solve()
{
trie.init();
int n = read();
for (int i = 0; i < n; i++) a[i] = read(); //题目要求下标从0开始。
int maxn = -2147483647; //最终的答案
for (int i = 0; i < n; i++)
{
int mx = trie.getDP(i) + 1;
upd(maxn, mx);
trie.insert(i, mx);
}
write(maxn), endl;
}
int main()
{
int T = read();
while (T--) solve();
return 0;
}
希望能帮助到大家!
首发:2022-08-24 17:00:47
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/282480.html