https://ac.nowcoder.com/acm/contest/11168/E
思路:可以反转一个区间代表着可以将任意两个连续的区间拼接在一起
于是问题就变成了选两个不相交的最大子区间的和最大是多少。考虑到值是log级别的,转化到值上去。
把一个合法区间的状态看成二进制,进行状压。由于指数级别,所以最多第二个for最多跑24次就结束了。
于是可以nlogn处理出序列里所有的二进制数。
问题就变成了求两个数x,y, x&y==0的条件下x^y的最大值。
于是预处理每个二进制状态在该区间里的最大子集是多少。记录为dp[j]:j的最大子集。
最后的答案就是 dp[j]+dp[ ( (1<<24 )-1) ^j]
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=1e5+100;
typedef long long LL;
inline LL read(){LL x=0,f=1;char ch=getchar(); while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;}
LL a[maxn];
LL dp[1<<25];///dp[j]:j的最大子集
int main(void)
{
LL n;n=read();
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
LL res=0;
for(int j=i;j<=min(n,(LL)i+27);j++){
if(a[j]&res) break;
res|=a[j];
dp[res]=res;
}
}
///预处理每一个二进制状态的子集的最大值
for(int i=0;i<24;i++){///枚举某一个二进制位置取反的子集;
for(LL j=0;j<(1<<24);j++){
if(( j&(1<<i) ) ){
dp[j]=max(dp[j],dp[j^(1LL<<i)]);
}
}
}
LL ans=0;
LL M=(1<<24)-1;
for(LL i=0;i<(1<<24);i++){
ans=max(ans,dp[i]+dp[M^i]);
}
printf("%lld\n",ans);
return 0;
}