我的做题思路参考的是这位大佬(代码就把他的抄了一遍)
https://blog.csdn.net/kzn2683331518/article/details/88768657
题面:
令LIS(S)为序列S的最长递增子序列的长度
给你n个非负整数,a[0],a[1],...,a[n-1],你可以对这个数组进行零次或多次操作,每次操作选择一个i(0<=i<=n-2),将a[i+1]变成(a[i+1] xor a[i])
你的任务是使得LIS(a)越大越好,输出LIS(a)的最大值
解题思路:
一步步来
1. 首先知道a^b^b = a 这个性质,并且一个数可以无限次异或前一位。
那么举个例子
a1 a2 a3 a4,请问a4可以得到哪几种异或的结果?
①:a2=a2^a1 --> a3=a3^(a2^a1) --> a4=a4^(a3^a2^a1),这是第一种
②:a3=a3^a2 --> a4=a4^(a3^a2),这是第二种
③:a4 = a4^a3,这是第三种
④ :在①的基础上a2 =(a2^a1)^a1=a2
a3 = (a3^a2^a1)^a2=a3^a1
a4 = (a4^a3^a2^a1)^(a3^a1) = a4^a2,这是第四种
⑤:对前面三项经过多次操作后,还可以得到a4^a1, a4^a1^a2, a4^a1^a3, a4^a2^a3...
于是归纳出结论:对于a[i],可以xjb乱异或前面的a[j] (j<i)
2. 构造线性基数组
还是举个例子吧:
给你五个数:
5 7 8 9 12
转化为二进制:
101 111 1000 1001 1100
亲,跟着我这么做:
首先构造一个lb[i][j]二维数组,表示是a[i]第j位上的线性基
现在我们假设求a[6]的线性基:
①先分析一下:
我们知道,暴力求出a[6]有多少种异或的方法并得到最小值需要2的五次方次(也就是2的(i-1)次方)计算
而利用线性基求固定63次运算(n很大的时候就省时间了)
63怎么得来的(long long型正数最大二进制下63位)
②开始构造a[6]的线性基,构造完再讲一下原理:
先扔进来101,从高到低遍历,第三位是1,判断lb[6][3]是否被赋值过(显然没有,还是初始化状态)
于是lb[6][3]=101(5),oj8k,直接return。
再扔进来111,再遍历,第三位是1,但lb[6][3]被赋值过,那怎么办?把111^lb[6][3],变成了010,
再判断第二位是1,lb[6][2]没有被赋值,于是lb[6][2]=010(2),oj8k.
然后打字好累长话短说,lb[6][4]=8
lb[6][1] = 1
最后的1100变成0,最后没有进入lb[6]的线性基
最后,看构造出来的lb[6]结果:
0101
0010
1000
0001
因为前五个数没有超过15的,所以只要四位就够了,这样四个每个都有一个1在各自位子上的数一定可以表示出这四个数异或出来的所有结果,而a[6]异或最大值只要将他的线性基从高到低遍历,如果当前线性基使得结果增大,那么就异或一下。
(这里具体我就说不清了,根据a^b^b=a这个性质应该可以证明)
3.构造dp数组:
前面求得了线性基,最终目的是修改a[i]的值,使其比a[i-1]大的同时尽可能小。
dp[i][j]表示前i位长度为j的lis最小后缀(注意LIS不必是连续的一串,中间可以断一下)
dp[i][j] = min (dp[i-1][j],计算得来的dp[i][j]);
4.关于代码里求比上一项大的最小值的那一坨代码(getminmax)的解释
看懂了代码其他部分再来看吧
先预设x为最大值,
if(x<g)滚,没有,最大的都不是
else {
x异或当前位,判断是否比g大,
大就判断和之前x谁大
小就对后面的基搞来搞去,搞出后面的基可以异或出的最大的数
如果比g大,那么此时最小的x就变成tmp
要问为什么?(因为我来了) x = same 1 xxxx >g
这个 tmp = same 0 yyyy > g
}
代码:(这个1要longlong 是真的挺坑的,我打赌以后自己还会错)
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
const int N = 105;
ll lb[N][65],dp[N][N],a[N];
void get_lb(int i,ll x)
{
for (int j = 62;j>=0;j--){
if ((x>>j)&1){
//if (((ll)1<<j)&x){//或者这样,但是记得longlong
if (!lb[i][j+1]){
lb[i][j+1] = x;
return ;
}
else x = x^lb[i][j+1];
}
}
}
ll getmin(int i,ll x)
{
ll ans = x;
for (int j=63;j>=1 && ans;j--){
ans = min(ans,ans^lb[i][j]);
}
return ans;
}
ll getminmax(int i,ll x)
{
ll ans = a[i];///不要写成 = x -_-||
for (int j=63;j>=1;j--) ans = max(ans,ans^lb[i][j]);
if (ans<=x) return INF;
for (int j=63;j>=1;j--){
ll temp = ans^lb[i][j];
if (temp>ans) continue;
if (temp>x) ans = temp;
else {
for (int k=j-1;k>=1;k--)
temp = max(temp,temp^lb[i][k]);
if (temp>x) ans = temp;
}
}
return ans;
}
int main()
{
int n;
while (~scanf("%d",&n)){
memset(lb,0,sizeof lb);
memset(dp,INF,sizeof dp);
for (int i=1;i<=n;i++) scanf("%lld",a+i);
for (int i=2;i<=n;i++){
memcpy(lb[i],lb[i-1],sizeof lb[i-1]);
get_lb(i,a[i-1]);
}
dp[1][1] = a[1];
for (int i=2;i<=n;i++) dp[i][1] = min(getmin(i,a[i]),dp[i-1][1]);
for (int i = 2;i<=n;i++)
for (int j=2;j<=i;j++)
if (dp[i-1][j-1]==INF) break;
else dp[i][j] = min(dp[i-1][j],getminmax(i,dp[i-1][j-1]));
/*
for (int i = 1;i<=n;i++){
for (int j=1;j<=i;j++){
if (dp[i][j]==INF) printf("dp[%d][%d]=INF ",i,j);
else printf("dp[%d][%d]=%lld ",i,j,dp[i][j]);
}
printf("\n");
}
*/
for (int i=n;i>=1;i--){
if (dp[n][i]!=INF){
printf("%d\n",i);
break;
}
}
}
return 0;
}