problem
cf链接
solution
读完题先直接暴力 dpdpdp 拿出来,dpi=maxj<i{dpj+(fi&fj)}dp_i=\max_{j<i}\big\{dp_{j}+(f_i\&f_j)\big\}dpi=maxj<i{dpj+(fi&fj)}。
谁能优化谁就是爸爸
假设存在 j<k<ij<k<ij<k<i,且 fi&fjf_i\&f_jfi&fj 为 111 的最高二进制位是 ppp,且 fi&fk,fk&fjf_i\&f_k,f_k\&f_jfi&fk,fk&fj 的第 ppp 位也为 111。
那么选择 j,k,ij,k,ij,k,i 肯定比 j,ij,ij,i 跳过 kkk 要优。
因为 fj&fif_j\&f_ifj&fi 比 ppp 更高的位全是 000,而加上 kkk,2p2^p2p 就会计入两次贡献,说不定更高位还要额外0贡献产生,但都一定比只算一次 2p2^p2p 大。
所以我们只需要对于每一个二进制位,记录最近的该位为 111 的数下标即可。
到时候直接用 fif_ifi 二进制也为 111 的那些位置进行转移,转移完后再更新。
dpi=maxfi>>j&1{dpgj+(fi&fgj)}dp_i=\max_{f_i>>j\&1}\big\{dp_{g_j}+(f_i\&f_{g_j})\big\}dpi=maxfi>>j&1{dpgj+(fi&fgj)}。
时间复杂度从 O(n2)O(n^2)O(n2) 锐减成 O(nlogv)O(n\log v)O(nlogv)。
位运算常考察两个点:拆成二进制位独立计算以及高二进制位决定/优先低二进制位。
code
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define maxn 1000005
int n;
int f[maxn], dp[maxn], g[60];signed main() {scanf( "%lld", &n );for( int i = 1;i <= n;i ++ ) scanf( "%lld", &f[i] );for( int i = 1;i <= n;i ++ ) {for( int j = 40;~ j;j -- )dp[i] = max( dp[i], dp[g[j]] + (f[g[j]] & f[i]) );for( int j = 40;~ j;j -- )if( f[i] >> j & 1 ) g[j] = i;}int ans = 0;for( int i = 1;i <= n;i ++ ) ans = max( ans, dp[i] );printf( "%lld\n", ans );return 0;
}
/*
dp[i]=max{ dp[j]+a[i]&a[j] }
consider k(j<k<i) satisfy the highest digit p a[i]&a[j] a[i]&a[k] a[j]&a[k] all 1
it's better to choose j k i instead of j i
calc 2^p twice is bigger than once,of course
we just need to choose the nearest node for each digit
O(n^2)->O(n \log v)
*/