[Codeforces 193B Xor]暴搜
分类:Brute force
1. 题目链接
2. 题意描述
给定四个长度为 n,下标从 1 到 n 的数组 a, b, k, p,保证 p[1], p[2], …, p[n] 是 1, 2, …, n 的一个排列。你要对数组 a 进行恰好 u 次操作,每次可以在以下两种操作中选择一种:
- 对所有 i = 1, 2, …, n,将 a[i] 修改为 a[i] xor b[i];
- 对所有 i = 1, 2, …, n,将 a[i] 修改为 a[p[i]] + r。
请问,u 次操作之后, 的值最大可能是多少?1 <= n, u <= 30。
3. 解题思路
闲来逛知乎,看到这么一个《刷题时遇到过哪些巧妙的搜索题?》的一个回答。故决定尝试求解一番。
暴搜是 O(2^u) 的,会 TLE。如何剪枝?看起来这个最优化的目标没什么规律?不过,注意到操作 1 连做两次和没做一样,所以问题可以转成:操作次数和 u 奇偶性相同,操作 1 不能连做两次,最大化 s。这样的复杂度满足递归式 T(u) = T(u-1) + T(u-2),为 Fibonacci 数级别增长的复杂度,可以通过 u = 30 的数据。——引自知乎答主rsa
操作一的异或具有一种特殊性。连续异或多次的结果只能异或次数的奇偶性有关。
然后,我考虑枚举第二种操作的次数,然后根据奇偶关系(而非第一种操作的次数)来枚举第一种操作。
思路很简单,下面来分析一下这样做的算法复杂度:
设第一种操作次数记为
,第二种操作次数记为
,有
。
然后用插板法。
个元素提供了
个空位。
把在空位中放入奇数个操作一
,看成往这个空位上放入一个元素。
然后有以下两种情况:
- 当 时,表示 个元素可以填满 个位置。那么,只需要暴力枚举这 个位置的奇偶性即可。复杂度是 。
- 否则,也就是说, 个位置不能全被填满,那么复杂度为 。这都是一个可以接受的复杂度。
不过答主所说的斐波那契
复杂度也很好理解~
4. 实现代码
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
typedef pair<long long, long long> pll;
const int inf = 0x3f3f3f3f;
const long long infl = 0x3f3f3f3f3f3f3f3fLL;
template<typename T> inline void umax(T &a, T b) { a = max(a, b); }
template<typename T> inline void umin(T &a, T b) { a = min(a, b); }
void debug() { cout << endl; }
template<typename T, typename ...R> void debug (T f, R ...r) { cout << "[" << f << "]"; debug (r...); }
const int MAXN = 35;
int n, u, r, numA, numB;
long long a[MAXN], b[MAXN], k[MAXN], p[MAXN];
long long ans;
void dfs(int cntA, int dep) {
if (cntA > numA) return;
long long t[MAXN], z[MAXN];
memcpy(t, a, sizeof(a));
if (dep == numB) {
if ((numA - cntA) & 1) {
for (int i = 0; i < n; ++i) {
a[i] = a[i] ^ b[i];
}
}
long long val = 0;
for (int i = 0; i < n; ++i) val += a[i] * k[i];
umax(ans, val);
if ((numA - cntA) & 1) {
memcpy(a, t, sizeof(t));
}
return;
}
for (int i = 0; i < n; ++i) a[i] = t[p[i]] + r;
dfs(cntA, dep + 1);
memcpy(a, t, sizeof(t));
for (int i = 0; i < n; ++i) z[i] = a[i] ^ b[i];
for (int i = 0; i < n; ++i) a[i] = z[p[i]] + r;
dfs(cntA + 1, dep + 1);
memcpy(a, t, sizeof(t));
}
int main() {
#ifdef ___LOCAL_WONZY___
freopen("input.txt", "r", stdin);
#endif // ___LOCAL_WONZY___
cin >> n >> u >> r;
for (int i = 0; i < n; ++i) cin >> a[i];
for (int i = 0; i < n; ++i) cin >> b[i];
for (int i = 0; i < n; ++i) cin >> k[i];
for (int i = 0; i < n; ++i) {
cin >> p[i];
-- p[i];
}
ans = -infl;
for (numB = 0; numB <= u; ++numB) {
numA = u - numB;
dfs(0, 0);
}
cout << ans << endl;
#ifdef ___LOCAL_WONZY___
cout << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC * 1000 << "ms." << endl;
#endif // ___LOCAL_WONZY___
return 0;
}