Codeforces Round 1033 (Div. 2) 个人题解(A-E)

A. Square of Rectangles

三个矩形拼成一个正方形有且只有两种情况:

  • 三个矩形宽度相同并列
  • 小的两个矩形宽度相同的并列,大的拼在一旁

分类讨论即可,注意讨论 \(l\)\(b\) 谁作为长方形的宽

#include<bits/stdc++.h>

using namespace std;
#define int long long
#define endl '\n'
typedef unsigned long long ull;
typedef long long ll;
const int N = 2e5 + 5, M = 2e5 + 5, LGN = 20, mod = 998244353, B = 131, inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f, P = 1000000711;

void lxllxs() {
    int l1, l2, l3, b1, b2, b3;
    cin >> l1 >> b1 >> l2 >> b2 >> l3 >> b3;
    swap(l1, l3);
    swap(b1, b3);
    if (l1 + l2 == l3 && b1 == b2 && b1 + b3 == l3) {
        cout << "YES" << endl;
        return;
    }
    if (b1 + b2 == b3 && l1 == l2 && l1 + l3 == b3) {
        cout << "YES" << endl;
        return;
    }
    if (b1 == b2 && b2 == b3 && l1 + l2 + l3 == b1) {
        cout << "YES" << endl;
        return;
    }
    if (l1 == l2 && l2 == l3 && b1 + b2 + b3 == l1) {
        cout << "YES" << endl;
        return;
    }
    cout << "NO" << endl;
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--)lxllxs();
    return 0;
}

B. Square Pool

模拟样例可以发现,只有球在对角线上并且沿着对角线的方向才能进洞,判断条件写清楚即可

#include<bits/stdc++.h>

using namespace std;
#define int long long
#define endl '\n'
typedef unsigned long long ull;
typedef long long ll;
const int N = 2e5 + 5, M = 2e5 + 5, LGN = 20, mod = 998244353, B = 131, inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f, P = 1000000711;

void lxllxs() {
    int n, s;
    cin >> n >> s;
    int res = 0;
    while (n--) {
        int dx, dy, x, y;
        cin >> dx >> dy >> x >> y;
        if (x + y == s && dx != dy)res++;
        else if (x == y && dx == dy)res++;
    }
    cout << res << endl;
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--)lxllxs();
    return 0;
}

C. Divine Tree

可以发现 \(1\leq d(i)\leq i\) ,所以 \(n\leq m\leq n*(n+1)/2\)

令所有的点 \(i\) 的父亲为 \(i+1\),此时所有的 \(d(i)\)\(i\), 和为 \(n*(n+1)/2\)

构造方法:令所有 \(a[i]=d(i)\),从编号从大到小的顺序枚举,每次如果 \(a[i]\) 总和超过 \(m\) 就尽可能地减小 \(a[i]\) (至少为1),然后将点 \(i\) 的子边断掉,点 \(a[i]\) 作为其父节点。显然,此时的 \(a[i]\) 一定等于 \(d(i)\)

#include<bits/stdc++.h>

using namespace std;
#define int long long
#define endl '\n'
typedef unsigned long long ull;
typedef long long ll;
const int N = 2e5 + 5, M = 2e5 + 5, LGN = 20, mod = 998244353, B = 131, inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f, P = 1000000711;

void lxllxs() {
    int n, m;
    cin >> n >> m;
    if (m < n || m > n * (n + 1) / 2) {
        cout << -1 << endl;
        return;
    }
    vector<int> ans(n + 1);
    int sum = n * (n + 1) / 2;
    for (int i = 1; i <= n; i++)ans[i] = i;
    int t = -1;
    for (int i = n; i >= 1; i--) {
        if (sum > m) {
            int p = min(sum - m, ans[i] - 1);
            ans[i] -= p;
            sum -= p;
        } else {
            t = i;
            cout << t << endl;
            break;
        }
    }
    for (int i = 1; i + 1 <= t; i++) {
        cout << i << " " << i + 1 << endl;
    }
    for (int i = t + 1; i <= n; i++) {
        cout << i << " " << ans[i] << endl;
    }
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--)lxllxs();
    return 0;
}

D. Matrix game

每个位置会出现 \(1\)~\(k\) 的数。在一列中,如果必须要出现相同的 \(a\) 个数字,根据鸽巢原理,至少要 \(n=k\times (a-1)+1\)​ 行。

每一列都至少会出现一组相同的 \(a\) 个数字。我们就以最坏的情况每次都为 \(1\) 来看,把这 \(a\) 个数以外的数都看成 \(0\)(因为无关紧要),以每列为单位来看,这又是一个鸽巢原理的问题:每列可能出现 \(k\times C_n^a\) 个向量,如果必须要出现相同的 \(b\) 个向量,至少要 \(m=k\times C_n^a\times (b-1)+1\)​ 列。

求组合数这里,虽然 \(n\) 很大,但 \(a\) 在可枚举范围内,所以直接枚举计算就行了。

#include<bits/stdc++.h>

using namespace std;
#define int long long
#define endl '\n'
typedef unsigned long long ull;
typedef long long ll;
const int N = 2e5 + 5, M = 2e5 + 5, LGN = 20, mod = 1e9 + 7, B = 131, inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f, P = 1000000711;

typedef __int128 i128;

int qmi(int a, int p) {
    int res = 1;
    while (p) {
        if (p & 1)res = res * a % mod;
        a = a * a % mod;
        p >>= 1;
    }
    return res;
}

void lxllxs() {
    int a, b, k;
    cin >> a >> b >> k;
    int ans1 = ((a - 1) * k + 1) % mod;
    int ans2 = 1;
    for (int i = 0, j = (a - 1) * k + 1; i < a; i++, j--) {
        ans2 = (i128) ans2 * j % mod;
    }
    for (int i = 0; i < a; i++) {
        ans2 = ans2 * qmi(i + 1, mod - 2) % mod;
    }
    ans2 = (ans2 * (b - 1) % mod * k % mod + 1) % mod;
    cout << ans1 << " " << ans2 << endl;
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--)lxllxs();
    return 0;
}

E. Lanes of Cars

总的愤怒值为 \(\sum \frac{a_i \times (a_i-1)}{2}+cnt\times k\)\(cnt\) 为切换车道的总次数

有一个很直接的贪心方式就是当 \(max\{a_i\}-min\{a_i\}>k+1\) 时,然后最大值减一,最小值加一。但是如果一次一次地操作,在 \(a_i\)​ 的范围下,肯定会超时。所以考虑通过二分边界(第一次),进行批量操作:二分操作完后的最大值+1的数

注:二分只是找到一个好处理的边界,不是所有操作的边界,所以二分的边界操作完后,还会有余量操作

二分完后可以得到操作的次数,然后如何将这些 \(1\) 一个一个全部加到最小值上? 同样的可以进行二分边界(第二次),批量操作:二分把这些 \(1\) 加上去后的最小值-1的数。二分后(第二次)会有几个余量的操作,可以直接枚举完成。

然后第一次二分得到的操作解决完后,也会有一些余量的操作,这部分可以用 \(multiset\) 暴力。

#include<bits/stdc++.h>

using namespace std;
#define int long long
#define endl '\n'
typedef unsigned long long ull;
typedef long long ll;
const int N = 2e5 + 5, M = 2e5 + 5, LGN = 20, mod = 1e9 + 7, B = 131, inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f, P = 1000000711;

void lxllxs() {
    int n, k;
    cin >> n >> k;
    vector<int> a(n);
    for (int i = 0; i < n; i++)cin >> a[i];
    sort(a.begin(), a.end());
    auto check1 = [&](int mid) {
        int ct1 = 0, ct0 = 0;
        for (int i = 0; i < n; i++) {
            if (a[i] > mid)ct1 += a[i] - mid;
            if (a[i] < mid - k)ct0 += mid - k - a[i];
        }
        return ct1 <= ct0;
    };
    int l = 1, r = 1e6;
    while (l < r) {
        int mid = l + r >> 1;
        if (check1(mid))r = mid;
        else l = mid + 1;
    }
    int res = 0, cnt = 0;
    for (int i = 0; i < n; i++) {
        if (a[i] > l) {

            res += (a[i] - l) * k;
            cnt += a[i] - l;
            a[i] = l;
        }
    }

    auto check2 = [&](int mid) {
        int ct = 0;
        for (int i = 0; i < n; i++) {
            if (a[i] < mid)ct += mid - a[i];
        }
        return ct <= cnt;
    };
    l = 1, r = 1e6;
    while (l < r) {
        int mid = l + r + 1 >> 1;
        if (check2(mid))l = mid;
        else r = mid - 1;
    }
    for (int i = 0; i < n; i++) {
        if (a[i] < l) {
            cnt -= l - a[i];
            a[i] = l;
        }
    }
    for (int i = 0; i < cnt; i++)a[i]++;
    multiset<int> se;
    for (int i = 0; i < n; i++) {
        se.insert(a[i]);
    }
    while (true) {
        auto p1 = *se.begin(), p2 = *se.rbegin();
        if (p2 - p1 > k + 1) {
            se.erase(se.find(p1));
            se.erase(se.find(p2));
            se.insert(p1 + 1);
            se.insert(p2 - 1);
            res += k;
        } else break;
    }
    for (auto x: se) {
        res += x * (x + 1) / 2;
    }
    cout << res << endl;
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--)lxllxs();
    return 0;
}
posted @ 2025-06-25 17:04  lxllxs  阅读(232)  评论(0)    收藏  举报