题目链接
Xor-Subsequence
题意
给定一个长度为
n
n
n 的数组,下标从0开始。现在要在数组中找到一个最长的子序列,满足子序列中相邻两项
a
[
i
]
a[i]
a[i]和
a
[
j
]
(
i
>
j
)
a[j]\ (i>j)
a[j] (i>j)有
a
j
⊕
i
<
a
i
⊕
j
a_j \oplus i < a_i \oplus j
aj⊕i<ai⊕j,求满足要求的最长子序列的长度。
分析
在简单版本中
a
[
i
]
<
=
200
a[i]<= 200
a[i]<=200,发现
a
[
i
]
a[i]
a[i]和
a
[
j
]
a[j]
a[j]只对异或结果的第八位有影响,当
i
−
j
>
256
i-j>256
i−j>256时,必有
a
j
⊕
i
>
a
i
⊕
j
a_j \oplus i > a_i \oplus j
aj⊕i>ai⊕j,因此只需在
i
−
j
<
=
256
i-j<=256
i−j<=256 时进行转移。时间复杂度
O
(
256
n
)
O(256n)
O(256n)。
在困难版本中 a [ i ] < = 1 0 9 a[i]<=10^9 a[i]<=109,暴力转移的时间复杂度为 O ( n 2 ) O(n^2) O(n2),需要发现一些性质进行求解。对于不等于的情况先将其转为等于进行考虑。 a j ⊕ i = a i ⊕ j a_j \oplus i = a_i \oplus j aj⊕i=ai⊕j 可得 a i ⊕ i = a j ⊕ j a_i \oplus i = a_j \oplus j ai⊕i=aj⊕j,设 b i = a i ⊕ i , b j = a j ⊕ j b_i=a_i \oplus i,b_j=a_j \oplus j bi=ai⊕i,bj=aj⊕j, a j ⊕ i < a i ⊕ j a_j \oplus i < a_i \oplus j aj⊕i<ai⊕j 说明从高位到低位前 k k k 位 b i b_i bi 和 b j b_j bj 相等,第 k + 1 k+1 k+1位 a i ⊕ j > a j ⊕ i a_i \oplus j > a_j \oplus i ai⊕j>aj⊕i,即第 k + 1 k+1 k+1位 a i ≠ j a_i \neq j ai=j且 a j = i a_j=i aj=i,分情况讨论。
a i a_i ai | j j j | a j a_j aj | i i i |
---|---|---|---|
0 | 1 | 0 | 0 |
0 | 1 | 1 | 1 |
1 | 0 | 0 | 0 |
1 | 0 | 1 | 1 |
考虑在字典树上存储 a i ⊕ i a_i \oplus i ai⊕i,在字典树上查询时需要知道下标信息,因此还需要存储下标 i i i,字典树上每个结点有两个状态0和1,表示第 k k k位上0/1状态的最大值。时间复杂度 O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))
AC代码
easy version
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long ll;
const int N=3e5+10;
int a[N],f[N],mx[N];
int n;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int T;
cin>>T;
while(T--)
{
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
for(int i=0;i<n;i++)
{
f[i]=1;
for(int j=i-1;j>=0;j--)
{
if(i-j>256) break;
if((a[i]^j)>(a[j]^i))
{
f[i]=max(f[i],f[j]+1);
}
}
}
int ans=0;
for(int i=0;i<n;i++) ans=max(ans,f[i]);
cout<<ans<<endl;
}
return 0;
}
hard version
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long ll;
const int N=3e5+10;
const int M=31*N;
int tr[M][2],dp[M][2];
int a[N],f[N];
int n,tot;
void init()
{
for(int i=0;i<=tot;i++)
{
memset(tr[i],0,sizeof(tr[i]));
memset(dp[i],0,sizeof(dp[i]));
}
tot=0;
for(int i=1;i<=n;i++) f[i]=0;
}
void insert(int x,int y)
{
int p=0;
for(int i=30;i>=0;i--)
{
int u=x>>i&1;
int z=y>>i&1;
if(!tr[p][u]) tr[p][u]=++tot;
p=tr[p][u];
dp[p][z]=max(dp[p][z],f[y]);
}
}
int query(int x,int k)
{
int ans=1;
int p=0;
for(int i=30;i>=0;i--)
{
int u=x>>i&1;
int y=tr[p][!u];
int z=k>>i&1;
ans=max(ans,dp[y][!z]+1);
if(!tr[p][u]) break;
p=tr[p][u];
}
return ans;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int T;
cin>>T;
while(T--)
{
cin>>n;
init();
for(int i=0;i<n;i++) cin>>a[i];
for(int i=0;i<n;i++)
{
f[i]=query(a[i]^i,a[i]);
insert(a[i]^i,i);
}
int ans=0;
for(int i=1;i<=n;i++) ans=max(ans,f[i]);
cout<<ans<<endl;
}
return 0;
}