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(); } }