题意
给一个数组,下标从[0,n-1],要你选一些索引[0,n-1],组成一个pos[]数组(升序的,假设有m个元素);
pos[]满足 a[pos[i]] ^ pos[i+1] < a[pos[i+1]] ^ pos[i] (i = [0,m-1]);
问最多可以选多少个下标,也就是问m的最大值.
分析
dp求最长序列。dp[i]表示到第i个数时,最长有多少。两层循环暴力求解的递推式为:dp[i] = max(dp[i], dp[j]+1); (0<=j<=i-1)。n的范围3e5显然超时,但是ai最大200,可以考虑到不论a[i]异或何值,只影响到了二进制下8位,也就是说完全可以缩小内层循环的枚举区间到 [max(i-256,0), i-1]。因为在这个区间出现的数,后8位包含了所有和a[i]异或的情况,贪心的只往前枚举这几个数即可。
代码
#include <bits/stdc++.h>
#define faster() ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
using namespace std;
const int N = 3e5+10;
int a[N],dp[N];
// 最多可以选择多少个下标
// m的最大值
void solve()
{
int n;
cin >> n;
for(int i = 0; i < n; i ++)
{
cin >> a[i];
dp[i] = 1;
}
for(int i = 0; i < n; i ++)
{
for(int j = i - 1; j >= max(0, i - 256); j--) //
{
if((a[i]^j) > (a[j]^i))
{
dp[i] = max(dp[i], dp[j]+1);
}
}
}
int ans = 0;
for(int i = 0; i < n; i++)
{
ans = max(ans, dp[i]);
}
cout << ans << endl;
}
int main()
{
faster();
int t = 1;
cin >> t;
while(t--)
{
solve();
}
}