codeforces round 735 
B. Cobb 
暴力做法  
想要最大化 f ( i , j ) f(i, j) f ( i , j )  ,其中 f ( i , j ) = i ⋅ j − k ⋅ ( a i ∣ a j ) f(i, j) = i \cdot j - k \cdot (a_i \mid a_j) f ( i , j ) = i ⋅ j − k ⋅ ( a i  ∣ a j  ) 
所以当 i ∈ [ n − 1 − 2 k , n ] i \in [n-1-2k, n] i ∈ [ n − 1 − 2 k , n ]   的时候,继续暴力求解,整个时间复杂度在 O ( k 2 ) O(k^2) O ( k 2 )   级别
状态压缩 dp 
可以考虑枚举 x = a i ∣ a j x = a_i \mid a_j x = a i  ∣ a j   ,问题就转换为如何从 x x x   中分离出 i , j i, j i , j  
注意分离出的 i , j i, j i , j   一定是最大的两个下标 
先遍历 a a a   数组,f ( x ) f(x) f ( x )   表示使得 a i = x a_i = x a i  = x   的最大下标  i i i  
枚举 x ∈ [ 0 , 2 n ] x \in [0, 2n] x ∈ [ 0 , 2 n ]  ,如果 x x x   的二进制 b b b   位为 1 1 1  ,那么取 x x x   的一个子集 x 2 x_2 x 2   
x 2 = x ⊕ ( 1 ≪ b ) x_2 = x \oplus (1 \ll b) x 2  = x ⊕ ( 1 ≪ b )  ,x 2 x_2 x 2    至少比 x x x   少了一个元素,所以一定是 x x x   的子集
观察 x , x 2 x, x_2 x , x 2   ,可以发现从状态 x 2 → x x_2 \to x x 2  → x  ,存在转移 
x x x   比 x 2 x_2 x 2    至少多一个元素 a i a_i a i   ,并且 a i a_i a i    的第 b b b   位为 1 1 1  
用 f ( x , 0 ) f(x, 0) f ( x , 0 )   表示能到达状态 x x x   的最大下标,f ( x , 1 ) f(x, 1) f ( x , 1 )   表示第二大的  
这样可以用 { f ( x 2 , 0 ) , f ( x 2 , 1 ) } \{f(x_2, 0), f(x_2, 1)\} { f ( x 2  , 0 ) , f ( x 2  , 1 ) }   来更新 { f ( x , 0 ) , f ( x , 1 ) } \{f(x, 0), f(x, 1)\} { f ( x , 0 ) , f ( x , 1 ) }  
于是可以写出状态压缩 dp
枚举 x ∈ [ 0 , 2 n ] x \in [0, 2n] x ∈ [ 0 , 2 n ]  ,对于 ∀ b ∈ [ 0 , 20 ] , x 2 = ( x ⊕ ( 1 ≪ b ) ) \forall b \in [0, 20], x_2 = (x \oplus (1 \ll b)) ∀ b ∈ [ 0 , 2 0 ] , x 2  = ( x ⊕ ( 1 ≪ b ) )  
{ f ( x 2 , 0 ) , f ( x 2 , 1 ) } → { f ( x , 0 ) , f ( x , 1 ) } \quad\{f(x_2, 0), f(x_2, 1)\} \to \{f(x, 0), f(x, 1)\} { f ( x 2  , 0 ) , f ( x 2  , 1 ) } → { f ( x , 0 ) , f ( x , 1 ) }  
如果 f ( x , 0 ) ≠ 0 f(x, 0) \neq 0 f ( x , 0 )  = 0   并且 f ( x , 1 ) ≠ 0 f(x, 1) \neq 0 f ( x , 1 )  = 0  ,用 f ( x , 0 ) ⋅ f ( x , 1 ) − k ⋅ x f(x, 0) \cdot f(x, 1) - k \cdot x f ( x , 0 ) ⋅ f ( x , 1 ) − k ⋅ x   更新 r e s res r e s 
上述算法思想是  
a i ∣ a j a_i \mid a_j a i  ∣ a j    可以用状态压缩,枚举每一个集合 x x x  ,并且用 x x x   的每一个子集  x 2 x_2 x 2    来更新 x x x  
对于当前集合 x x x  ,( i , j ) (i, j) ( i , j )   一定对应集合 x x x   中最大的两个数的下标 
于是只需要考虑 x x x   中 f ( x , 0 ) f(x, 0) f ( x , 0 )   和 f ( x , 1 ) f(x, 1) f ( x , 1 )   是否合法? 
如果合法,用 f ( x , 0 ) ⋅ f ( x , 1 ) − k ⋅ x f(x, 0) \cdot f(x, 1) - k \cdot x f ( x , 0 ) ⋅ f ( x , 1 ) − k ⋅ x   来更新全局的 a n s ans a n s 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 const int maxn = 2e5 + 10; const ll inf = 0x3f3f3f3f3f3f3f3f; int f[maxn][2], n, k; ll a[maxn], ans = -inf; void init  () {     memset(f, 0, sizeof f);     ans = -inf; } inline void upd(const int x, const int x2) {     if  (f[x2][0] > f[x][0]) {         int t = f[x][0];         f[x][0] = f[x2][0], f[x][1] = t;     }     else  if  (f[x2][0] != f[x][0] && f[x2][0] > f[x][1]) f[x][1] = f[x2][0];     if  (f[x][1]) assert(f[x][0] != f[x][1]); } void pre  () {     for  (int i = 1; i <= n; i++) {         ll x = a[i];         if  (i > f[x][0]) {             f[x][1] = f[x][0], f[x][0] = i;         }         else  if  (i != f[x][0] && i > f[x][1]) f[x][1] = i;     } } void solve  () {     for  (int x = 0; x <= 2 * n; x++) {         for  (int b = 0; b < 20; b++) {             if  ((x >> b & 1) == 0) continue ;             int x2 = x ^ (1<<b);              upd(x, x2);         }         ll res = 1ll * f[x][0] * f[x][1] - k * x;         if (f[x][0] && f[x][1] && res > ans) ans = res;     }     printf("%lld\n", ans); } int main() {     freopen("input.txt", "r", stdin);     int cas;     cin >> cas;     while (cas--) {         scanf("%d%d", &n, &k);         for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);         init();         pre();         solve();     } }