https://codeforces.com/contest/2061/problem/E
题目大意
给出 $n \ (1 \le n \le 10^5)$ 长的数组 $a$ 和 $m \ (1 \le m \le 10)$ 长的数组 $b$。满足 $0 \le a_i, b_i < 2^{30}$。
可以执行以下操作 $k \ (0 \le k \le nm)$ 次:
选取 $a_i$ 和 $b_j$ 将 $a_i$ 变为 $a_i \ \text{&} \ b_j$。
问执行完这些操作最小的数组和是多少。
题解
我们可以求出每个数 $a_i$ 上执行 $j$ 次操作的最小结果。显然这个结果是单调不增的。易证:我们总可以在选 $j$ 个基础上任意选一个没选过的,由于与的性质,这个结果一定不会增大。所以最优解一定小于等于这个特殊解。
特别注意:第在 $a_i$ 上进行 $j + 1$ 次操作的最好结果不一定是 $j$ 次操作最好结果的基础上再进行某一次!例如 $a_i = 1111_{2}$,如果 $b = {0101_2, 1001_2, 0110_2}$,则选取 $1$ 个的时候是选第一个最优,而 $2$ 个的时候是后两个。因此这个步骤,必须枚举 $b_i$ 的全部可能组合。
引理
我们可以求出每多用一步的差量。这里更重要的性质是,对于每个 $a_i$ 这个差量也是单调不增的。
证明
设 $f(S)$ 表示 $a_i$ 与上 $S$ 集合之中所有数的结果。
$g(j)$ 为执行 $j$ 步的后 $a_i$ 的最小结果,这里相当于要证明 $g(j)$ 是下凸的,即 $2g(i) \le g(i - 1) + g(i + 1)$。
设 $S$ 为 $i - 1$ 次操作最好的集合,$T$ 为 $i + 1$ 次操作最好的集合。$g(i - 1)$ 与 $g(i + 1)$ 最高的不同的位为第 $w$ 位(所有位都一样的情况,由于上述证明,我们知道等号成立)。则我们可以得出我们总能找到一个 $y \in T \setminus S$,使得我们可以通过 $y$ 把 $g(i - 1)$ 的 $i$ 位变为 $0$。即
$$ g(i - 1) - f(S \cup \{y\}) \ge 2^w. $$另一边,由于 $f(S \cup {y})$ 的 $w$ 位与 $g(i + 1)$ 的 $w$ 位都为 $0$,则
$$ f(S \cup \{y\}) - g(i + 1) < 2^w. $$综上可以得到
$$ 2g(i) \le 2f(S \cup \{y\}) \le g(i - 1) + g(i + 1). $$得证。
有了这个性质,我们按照从大到小的顺序贪心的选就可以了。
(比赛中没有完全想清楚,写了 $n$ 路归并,但其实这里是没必要的。直接排序甚至 $std::nth_element$ 即可。)
const int INF = (1 << 30) + 1;
const int MN = 1e5 + 5;
const int MM = 10;
int diff[MN][MM];
int m;
struct Node {
int d, id, cur;
bool operator<(const Node& B) const {
if (d != B.d) return d < B.d;
for (int i = cur, j = B.cur; i < m && j < m; i++, j++) {
if (diff[id][i] != diff[B.id][j]) return diff[id][i] < diff[B.id][j];
}
return cur > B.cur;
}
};
void solve() {
int n, k; cin >> n >> m >> k;
vector<int> a(n);
vector<int> b(m);
for (int& i : a) cin >> i;
for (int& i : b) cin >> i;
LL ans = 0;
for (int i : a) ans += i;
int msk = 1 << m;
vector<int> bb(msk, (1 << 30) - 1);
for (int i = 1; i < msk; i++) {
for (int j = 0; j < m; j++) {
if (i & (1 << j)) bb[i] &= b[j];
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
diff[i][j] = INF;
}
for (int j = 1; j < msk; j++) {
cmin(diff[i][__builtin_popcount(j) - 1], a[i] & bb[j]);
}
for (int j = m - 1; j > 0; j--) {
diff[i][j] = diff[i][j - 1] - diff[i][j];
}
diff[i][0] = a[i] - diff[i][0];
}
priority_queue<Node> qu;
for (int i = 0; i < n; i++) {
qu.push({diff[i][0], i, 0});
}
for (int i = 0; i < k; i++) {
auto [d, id, cur] = qu.top(); qu.pop();
if (!d) break;
ans -= d;
cur++;
if (cur < m) {
qu.push({diff[id][cur], id, cur});
}
}
cout << ans << '\n';
}
复杂度
$T$:$O(n 2^m + nm)$
$S$:$O(n + m)$
代码实现
#include <bits/stdc++.h>
using namespace std;
int io_=[](){ ios::sync_with_stdio(false); cin.tie(nullptr); return 0; }();
using LL = long long;
using ULL = unsigned long long;
using LD = long double;
using PII = pair<int, int>;
using VI = vector<int>;
using MII = map<int, int>;
template<typename T> void cmin(T &x,const T &y) { if(y<x) x=y; }
template<typename T> void cmax(T &x,const T &y) { if(x<y) x=y; }
template<typename T> bool ckmin(T &x,const T &y) {
return y<x ? (x=y, true) : false; }
template<typename T> bool ckmax(T &x,const T &y) {
return x<y ? (x=y, true) : false; }
template<typename T> void cmin(T &x,T &y,const T &z) {// x<=y<=z
if(z<x) { y=x; x=z; } else if(z<y) y=z; }
template<typename T> void cmax(T &x,T &y,const T &z) {// x>=y>=z
if(x<z) { y=x; x=z; } else if(y<z) y=z; }
// mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
// mt19937_64 rnd_64(chrono::system_clock::now().time_since_epoch().count());
/*
---------1---------2---------3---------4---------5---------6---------7---------
1234567890123456789012345678901234567890123456789012345678901234567890123456789
*/
const int INF = (1 << 30) + 1;
void solve() {
int n, m, k; cin >> n >> m >> k;
vector<int> a(n);
vector<int> b(m);
for (int& i : a) cin >> i;
for (int& i : b) cin >> i;
LL ans = 0;
for (int i : a) ans += i;
int msk = 1 << m;
vector<int> bb(msk, (1 << 30) - 1);
for (int i = 1; i < msk; i++) {
for (int j = 0; j < m; j++) {
if (i & (1 << j)) bb[i] &= b[j];
}
}
vector<int> f(m);
vector<int> diff;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
f[j] = INF;
}
for (int j = 1; j < msk; j++) {
cmin(f[__builtin_popcount(j) - 1], a[i] & bb[j]);
}
for (int j = m - 1; j > 0; j--) {
diff.push_back(f[j - 1] - f[j]);
}
diff.push_back(a[i] - f[0]);
}
nth_element(diff.begin(), diff.begin() + (n * m - k), diff.end());
for (int i = 0; i < k; i++) {
ans -= diff[n * m - i - 1];
}
cout << ans << '\n';
}
int main() {
int t = 1;
cin >> t;
while (t--) {
solve();
}
return 0;
}
对拍
一组小数据
1
4 6 4
20 27 23 4
7 22 31 14 22 13
对拍程序
#include <bits/stdc++.h>
using namespace std;
int io_=[](){ ios::sync_with_stdio(false); cin.tie(nullptr); return 0; }();
using LL = long long;
using ULL = unsigned long long;
using LD = long double;
using PII = pair<int, int>;
using VI = vector<int>;
using MII = map<int, int>;
template<typename T> void cmin(T &x,const T &y) { if(y<x) x=y; }
template<typename T> void cmax(T &x,const T &y) { if(x<y) x=y; }
template<typename T> bool ckmin(T &x,const T &y) {
return y<x ? (x=y, true) : false; }
template<typename T> bool ckmax(T &x,const T &y) {
return x<y ? (x=y, true) : false; }
template<typename T> void cmin(T &x,T &y,const T &z) {// x<=y<=z
if(z<x) { y=x; x=z; } else if(z<y) y=z; }
template<typename T> void cmax(T &x,T &y,const T &z) {// x>=y>=z
if(x<z) { y=x; x=z; } else if(y<z) y=z; }
// mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
// mt19937_64 rnd_64(chrono::system_clock::now().time_since_epoch().count());
template<typename Func> struct YCombinatorResult {
Func func;
template<typename T>
explicit YCombinatorResult(T &&func) : func(std::forward<T>(func)) {}
template<class ...Args> decltype(auto) operator()(Args &&...args) {
return func(std::ref(*this), std::forward<Args>(args)...);
}
};
template<typename Func> decltype(auto) y_comb(Func &&fun) {
return YCombinatorResult<std::decay_t<Func>>(std::forward<Func>(fun));
}
/*
---------1---------2---------3---------4---------5---------6---------7---------
1234567890123456789012345678901234567890123456789012345678901234567890123456789
*/
const int INF = 0x3f3f3f3f;
void solve() {
int n, m, k; cin >> n >> m >> k;
vector<int> a(n);
vector<int> b(m);
for (int& i : a) cin >> i;
for (int& i : b) cin >> i;
vector<int> curi(k), curj(k);
vector<int> ansi, ansj;
int mn = INF;
LL ans = y_comb([&](auto dfs, int cur) -> LL {
if (cur == k) {
LL ans = 0;
// for (int i : a) cerr << i << ' ';
// cerr << endl;
for (int i : a) ans += i;
if (ans < mn) {
mn = ans;
ansi = curi;
ansj = curj;
}
return ans;
}
LL ans = INF;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
curi[cur] = i;
curj[cur] = j;
int t = a[i];
a[i] &= b[j];
cmin(ans, dfs(cur + 1));
a[i] = t;
}
}
return ans;
})(0);
for (int i : ansi) cerr << i << ' ';
cerr << '\n';
for (int i : ansj) cerr << i << ' ';
cerr << '\n';
cout << ans << '\n';
}
int main() {
int t = 1;
cin >> t;
while (t--) {
solve();
}
return 0;
}
数据生成
#include <bits/stdc++.h>
using namespace std;
int io_=[](){ ios::sync_with_stdio(false); cin.tie(nullptr); return 0; }();
using LL = long long;
using ULL = unsigned long long;
using LD = long double;
using PII = pair<int, int>;
using VI = vector<int>;
using MII = map<int, int>;
template<typename T> void cmin(T &x,const T &y) { if(y<x) x=y; }
template<typename T> void cmax(T &x,const T &y) { if(x<y) x=y; }
template<typename T> void cmin(T &x,T &y,const T &z) {// x<=y<=z
if(z<x) { y=x; x=z; } else if(z<y) y=z; }
template<typename T> void cmax(T &x,T &y,const T &z) {// x>=y>=z
if(x<z) { y=x; x=z; } else if(y<z) y=z; }
mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
mt19937_64 rnd_64(chrono::system_clock::now().time_since_epoch().count());
/*
---------1---------2---------3---------4---------5---------6---------7---------
1234567890123456789012345678901234567890123456789012345678901234567890123456789
*/
inline int rndi() { return rnd(); }
inline int rndi(int n) { return rnd()%n; }
inline int rndi(int l, int r) { return rnd() % (r - l + 1) + l; }
inline LL rndll() { return rnd_64(); }
inline LL rndll(LL n) { return rnd_64() % n; }
inline LL rndll(LL l, LL r) { return rnd_64() % (r - l + 1) + l; }
inline char rndc() { return rnd() % 26 + 'a'; }
inline char rndC() { return rnd() % 26 + 'A'; }
inline char rnddig() { return rnd() % 10 + '0'; }
inline char rndcha() {
int v = rnd() % 52;
return v < 26 ? v + 'a' : (v - 26 + 'A');
}
template<typename T>
void shuffle(vector<T>& vec) { shuffle(vec.begin(), vec.end(), rnd); }
vector<int> rnd_permu(int n, int from = 0) {
vector<int> vec(n);
iota(vec.begin(), vec.end(), from);
shuffle(vec);
return vec;
}
void solve() {
int n = rndi(1, 5), m = rndi(1, 6), k = rndi(0, min(n * m, 5));
cout << n << ' ' << m << ' ' << k << '\n';
for (int i = 0; i < n; i++) {
cout << rndi(0, (1 << 5)) << ' ';
}
cout << '\n';
for (int i = 0; i < m; i++) {
cout << rndi(0, (1 << 5)) << ' ';
}
cout << '\n';
}
int main() {
int t = 10; cout << t << '\n';
while (t--) {
solve();
}
return 0;
}
Next: [CF] D. Game With Triangles - Codeforces Round 1000 (Div. 2)