2023.1.16
A.Cleaning the Phone
Link
https://codeforces.com/contest/1475/problem/D
Statement
给定 \(n (1 \leq n \leq 2 \cdot 10 ^ 5)\) 个物品,每个物品均有两个属性 \(a_i, b_i(1 \leq b_i \leq 2)\),现在需要确定一个序列 \(x_1, x_2, \cdots x_k\),使得在满足 \(\sum _{i = 1} ^ k a_{x_i} \geq m\) 的前提下 \(\sum _{i = 1} ^ k b_{x_i}\) 最小。若可以则输出对应的 \(\sum _{i = 1} ^ k b_{x_i}\)。
Solution
考虑到 \(b_i\) 的取值只有两种,结合“定一动一”的思想,我们可以将物品按照 \(b_i\) 分类后,分别按照 \(a_i\) 的大小倒序排序,枚举选前 \(i\) 个 \(b_i=1\)的物品,二分求得满足前提的最小的需要选择的 \(b_i = 2\) 的物品的个数,进行统计即可。
Code
#include <bits/stdc++.h>
typedef long long ll;
const int INF = 1 << 30;
const int N = 2e5 + 5;
using namespace std;
int T, n, m, num[3], a[N], b[N], c[3][N];
ll sum[3][N];
bool check(int p1, int p2) { return sum[1][p1] + sum[2][p2] >= m; }
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
freopen("in","r", stdin);
cin >> T;
while (T--) {
ll tot = 0;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
tot += a[i];
}
for (int i = 1; i <= n; i++) {
cin >> b[i];
c[b[i]][++num[b[i]]] = a[i];
}
if (tot < m) {
cout << "-1" << "\n";
num[1] = num[2] = 0;
continue;
}
for (int i = 1; i <= 2; i++) sort(c[i] + 1, c[i] + num[i] + 1, greater<int>());
for (int i = 1; i <= 2; i++) {
for (int j = 1; j <= num[i]; j++)
sum[i][j] = sum[i][j - 1] + c[i][j];
}
int ans = INF;
for (int i = 0; i <= num[1]; i++) {
int l = 0, r = num[2], pos = -1;
while (l <= r) {
int mid = (l + r) >> 1;
if (check(i, mid)) {
pos = mid;
r = mid - 1;
}
else l = mid + 1;
}
if (pos != -1) ans = min(ans, i + 2 * pos);
}
cout << ans << "\n";
num[1] = num[2] = 0;
}
return 0;
}
B.Unusual Matrix
Link
https://codeforces.com/contest/1475/problem/F
Statement
给定两个 \(n \times n\) 的 01
矩阵 \(A, B\),定义一种操作:选择矩阵 \(A\) 的第 \(i\) 行或第 \(j\) 列,将这一行/这一列上的所有数字全部与 \(1\) 异或。询问是否在一定次数的操作后,可以使 \(A = B\)。
Solution
关键性质是:
1.异或运算与次序无关
2.\(a_{i, j}\) 只有与 \(1\) 异或奇数次才会改变,故某一行或某一列最多需要使用该操作一次
结合以上两条性质,我们只需先考虑第一行的数字,找出需要异或的列;之后考虑第一列的数字,找出需要异或的行。最后判断 \(A\) 是否等于 \(B\) 即可。
Code
#include <bits/stdc++.h>
const int N = 1050;
using namespace std;
int T, n, a[N][N], b[N][N], flagi[N], flagj[N];
char c[N][N], d[N][N];
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
freopen("in","r", stdin);
cin >> T;
while (T--) {
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cin >> c[i][j];
a[i][j] = c[i][j] - '0';
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cin >> d[i][j];
b[i][j] = d[i][j] - '0';
}
}
for (int j = 1; j <= n; j++) flagj[j] |= (a[1][j] != b[1][j]);
for (int i = 2; i <= n; i++) {
if (flagj[1]) flagi[i] |= ((a[i][1] ^ 1) != b[i][1]);
else flagi[i] |= (a[i][1] != b[i][1]);
}
bool flag = false;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
a[i][j] = (flagi[i] + flagj[j] == 1) ? (a[i][j] ^ 1) : a[i][j];
if (a[i][j] != b[i][j]) {
flag = true;
break;
}
}
if (flag) break;
}
flag ? cout << "No" << "\n" : cout << "Yes" << "\n";
memset(flagi, 0, sizeof(flagi));
memset(flagj, 0, sizeof(flagj));
}
return 0;
}
C.Sticks
Link
https://codeforces.com/gym/103652/problem/K
Statement
给定 \(12\) 根火柴的长度,每根火柴只能使用一次,确定其最大能表示出多少个三角形,并输出方案。\((1 \leq T \leq 6000)\)
Solution
考虑状态压缩以及使用位运算来简化过程。用一个长度为 \(12\) 的二进制串,第 \(i\) 位表示第 \(i\) 根火柴是否被使用。则一个合法的三角形的状态,二进制表示下会有 \(3\) 个 \(1\)。首先统计出所有可能的三角形的状态,之后判断这些状态是否互相不干扰。考虑枚举,假设两个不相同的三角形状态分别为 \(s,t\),他们互不干扰的充要条件是 \(s \& t = 0\)。若他们互不干扰,则可以保证答案至少为 \(2\)。此时可以查询他们的补集是否出现且合法,若是则答案为 \(4\),直接输出方案即可,同时记录下这个答案为 \(2\) 的方案。若否,考虑构造 \(3\) 的可能方案。
枚举 \(3\) 的可能方案时,可以枚举哪 \(3\) 根火柴不用和 \(1\) 组的情况,再判断他们的补集是否出现过(剩下 \(2\) 组)。
Code
#include <bits/stdc++.h>
const int lim = 1 << 14;
using namespace std;
int T, a[20], pii[lim];
vector<int> f;
void print(int now) {
vector<int> out(5);
out.clear();
for (int i = 0; i < 12; i++) {
if ((now >> i) & 1) out.push_back(a[i]);
}
for (int i = 0; i < out.size(); i++) {
cout << out[i];
if (i != out.size() - 1) cout << " ";
}
cout << endl;
}
void solve() {
// 12! / (3! 3! 3! 3! 4!) = 15400
int ans = 0, one = 0, two = 0, st = (1 << 12) - 1;
f.clear();
memset(pii, 0, sizeof(pii));
for (int i = 0; i < 12; i++) {
for (int j = i + 1; j < 12; j++) {
for (int k = j + 1; k < 12; k++) {
if (a[i] + a[j] > a[k] && a[j] + a[k] > a[i] && a[i] + a[k] > a[j]) {
int sta = (1 << i) | (1 << j) | (1 << k);
f.push_back(sta);
ans = 1;
one = sta;
}
}
}
}
for (auto s : f) {
for (auto t : f) {
if (!(s & t)) {
ans = 2;
two = s | t;
pii[s | t] = s;
int op = st ^ (s | t);
if (pii[op]) {
cout << "4" << "\n";
print(s); print(t);
print(pii[op]); print(op ^ pii[op]);
return ;
}
}
}
}
for (int i = 0; i < 12; i++) {
for (int j = i + 1; j < 12; j++) {
for (int k = j + 1; k < 12; k++) {
int sta = (1 << i) | (1 << j) | (1 << k);
for (auto r : f) {
if (r & sta) continue;
int tw = st ^ (sta | r);
if (pii[tw]) {
cout << "3" << "\n";
print(r);
print(pii[tw]);
print(tw ^ pii[tw]);
return ;
}
}
}
}
}
cout << ans << "\n";
if (ans == 0) return ;
else if (ans == 1) print(one);
else print(pii[two]), print(two ^ pii[two]);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
freopen("in","r", stdin);
cin >> T;
for (int t = 1; t <= T; t++) {
for (int i = 0; i < 12; i++) cin >> a[i];
cout << "Case #" << t << ": ";
solve();
}
return 0;
}