2023.1.16

A.Cleaning the Phone

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

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

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;
}
posted @ 2023-01-18 02:31  BeyondLimits  阅读(34)  评论(0编辑  收藏  举报