题目描述
令LIS(S)为序列S的最长递增子序列的长度
给你n个非负整数,,你可以对这个数组进行零次或多次操作,每次操作选择一个,将变成, ^ 表示按位异或
你的任务是使得LIS(a)越大越好,输出LIS(a)的最大值
输入
第一行输入一个整数n (1 <= n <= 100)
第二行输入n个整数ai(0 <= ai <= 10^18)
输出
输出一个整数,即LIS(a)的最大值
样例输入
【样例输入1】
3
4 2 1
【样例输入2】
10
0 0 0 0 0 0 0 0 0 0
【样例输入3】
10
1 2 3 4 5 6 7 8 9 10
【样例输入4】
15
1008 42 7 5 2 9 75 0 0 12 3 6 81 4 3
样例输出
【样例输出1】
3
【样例输出2】
1
【样例输出3】
10
【样例输出4】
12
题目分析:
在题意中,我们能够令任意的变成,
也可以说成是令任意的变成。
也就是说每个数可以异或上他前面的第一个数,
由于异或有的性质,我们可以通过归纳发现
任意的都能够在不改变其他位置的情况下异或上
相当于每个位置都有一组它可以任意按需异或的集合,
这就符合线性基的特性,我们考虑对每个位置建立一组线性基
在每个位置都有一组线性基,说明这个位置有一些若干个取值,是我们能够按需任意选取的。
因为数据很小,我们可以用最差劲的的求解LIS的思路来考虑,其实也就是暴力dp
用表示的最小结尾数字。
如果我们找到一个最小的,满足
那么根据最长上升子序列的性质,我们可以得到:
现在问题是如何找到当前位置的可能取值中最小的,满足
贪心思路:
先将设置为当前位置的最大值,如果显然不存在合法的,返回INF
否则按位去尝试减小是否可行:
优先尝试减小的位置应该是恰好小于当前中二进制位为的最高的基底,先将这个基底在中删去:令
然后用去与作比较
如果,则令。
如果,这个时候并不一定就说明当前二进制位不能被设置为0
因为中小于的基底可能还未被设置为1,
我们应该贪心先将基底能够设置的位全部设置为1之后,再次判断和的关系:
这个时候如果仍然,才说明不能删去这个基底。
反之如果,则说明这个基底j是可被删去的令。
重复以上贪心过程直到遍历完改位置线性基全部的基底即可。
以上贪心过程中,每个基底会被遍历一次,贪心tmp取最大值的过程中又会遍历一次,复杂度为
加上dp的,总时间复杂度为//O(能过)
代码:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll INF = 0x3f3f3f3f3f3f3f3f;
int n,k;
ll b[110][70]; //b[i][j]位置i,第j位的基
ll lis[110][110]; //lis[i][j]位置i,lis为j的最小后缀
ll a[110];
void inser(int i, ll x){ //在位置i的线性基中插入值x
for(ll j = 62;j>=0 && x;j--){
if((x>>j)&1){
if(b[i][j] == 0){
b[i][j] = x; return;
}
else x ^= b[i][j];
}
}
}
ll getmin(int i){ //在位置i中能取得的最小取值
ll x = a[i];
for(ll j = 62;j>=0 && x;j--)
x = min(x, x^b[i][j]);
return x;
}
ll greatthan(int i, ll g){ //在位置i中找到最小的大于g的取值
ll x = a[i];
for(ll j = 62;j>=0;j--) //先将x设置为MAX取值
x = max(x, x^b[i][j]);
if(x > g){
for(ll j = 62;j>=0;j--)
if(b[i][j]){ //找到位置i的一个基底j
ll tmp = x^b[i][j];
if(tmp > x)continue;
if(tmp > g){
x = min(tmp, x);
continue;
}
else{
for(ll k = j-1;k>=0;k--)
tmp = max(tmp, tmp ^ b[i][k]);
if(tmp > g)
x = tmp;
}
}
assert(x > g);
return x;
}
else return INF;
}
int main() {
while(~scanf("%d\n",&n)){
memset(a,0,sizeof a);
memset(b,0,sizeof b);
memset(lis, INF, sizeof lis);
for(int i = 1;i<=n;i++)
scanf("%lld",&a[i]);
for(int i = 1;i <= n;i++){
memcpy(b[i],b[i-1],sizeof(b[i]));
inser(i, a[i-1]);
}
lis[1][1] = getmin(1);
for(int i = 2;i<=n;i++){
lis[i][1] = min(lis[i-1][1], getmin(i));
for(int j = 2;j<=i;j++){
if(lis[i-1][j-1] == INF)break;
lis[i][j] = min(lis[i-1][j], greatthan(i, lis[i-1][j-1]));
}
}
int ans = n;
for(int i = n;i>=1;i--){
if(lis[n][i] != INF){
ans = i;
break;
}
}
assert(ans >= 1);
printf("%d\n",ans);
}
return 0;
}