CodeForces Round #959 sponsored by NEAR (Div. 1 + Div. 2) 补题记录(A~E)

简单场.png

A

\(n\times m=1\) 则显然无解。否则因为 \(a\) 矩阵是一个 \(n\times m\) 的排列,所以说只需要让其循环右移一位即可。

时间复杂度 \(O(nm)\)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 500100;
int a[233][233];
signed main() {
    int T;
    cin >> T;
    while (T--) {
        int n, m;
        cin >> n >> m;
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j)
                cin >> a[i][j];
        if (n == 1 && m == 1)
            puts("-1");
        else {
            for (int i = 1; i <= n; ++i) {
                for (int j = 1; j <= m; ++j) {
                    int v;
                    if (j != m) v = a[i][j + 1];
                    else if (i != n) v = a[i + 1][1];
                    else v = a[1][1];
                    cout << v << ' ';
                }
                cout << '\n';
            }
        }
    }
    return 0;
}

B

手模几组数据之后可以发现:

  • 若两个字符串已经相等则一定成立。
  • 若两个字符串不相等,则令 \(s\) 串中第一个是 \(1\) 的位置 \(p_1\)\(t\) 串中第一个是 \(1\) 的位置 \(p_2\)。若有 \(p_1\le p_2\) 则有解,否则无解。

时间复杂度为 \(O(n)\)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 500100;
int a[233][233];
signed main() {
    int T;
    cin >> T;
    while (T--) {
        int n;
        cin >> n;
        string s, t;
        cin >> s >> t;
        if (s == t)
            puts("Yes");
        else {
            for (int i = 0; i < n; ++i) {
                if (s[i] == '1') {
                    puts("Yes");
                    break;
                } else if (t[i] == '1') {
                    puts("No");
                    break;
                }
            }
        }
    }
    return 0;
}

C

简单 dp 题。正着做不好处理,考虑倒着做。

\(f_i\) 表示若左端点 \(l=i\) 的时候,不同满足条件的 \(r\) 的数量。则:

  • \(a_i>x\)\(f_i=f_{i+1}\)
  • \(\sum\limits_{j=i}^n a_j\le x\)\(f_i=n-i+1\)
  • 否则令 \(p\) 为最小的满足 \(\sum\limits_{j=i}^p a_j>x\) 的位置 \(p\)。则有 \(f_i=p-i+f_{p+1}\)

前面两个转移都是容易的,第三个转移直接维护一下前缀和,然后二分找到 \(p\) 即可。

时间复杂度为 \(O(n\log n)\)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 500100;
int a[N], s[N], f[N];
signed main() {
    int T;
    cin >> T;
    while (T--) {
        int n, x;
        cin >> n >> x;
        for (int i = 1; i <= n; ++i)
            cin >> a[i], s[i] = s[i - 1] + a[i];
        f[n + 1] = f[n + 2] = 0;
        int sum = 0;
        for (int i = n; i; --i) {
            int l = i, r = n, best = -1;
            while (l <= r) {
                int mid = l + r >> 1;
                if (s[mid] - s[i - 1] <= x)
                    best = mid, l = mid + 1;
                else
                    r = mid - 1;
            }
            if (best == -1)
                sum += (f[i] = f[i + 1]);
            else
                sum += (f[i] = best - i + 1 + f[best + 2]);
        }
        cout << sum << '\n';
    }
    return 0;
}

D

考虑倒着处理。首先若 \(a_i=a_j\)\(|a_i-a_j|=0\),直接把这些边给连接。

然后对于剩下的一些倍数关系。若当前枚举的倍数关系为 \(d\),则按照 \(a_i\bmod d\) 的值将当前所有的 \(a_i\) 分为 \(d\) 类。然后随便找到两个 \(i\) 满足它们都在同一类里面,且当前没有连通,并将它们连通。判断两个点是否连通可以使用并查集。

时间复杂度为 \(O(\alpha n^2)\)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 500100;
int a[N], fa[N];
vector<int> b[N];
int find(int x) {
    return x == fa[x] ? x : fa[x] = find(fa[x]);
}
signed main() {
    srand(time(0));
    int T;
    cin >> T;
    while (T--) {
        int n;
        cin >> n;
        for (int i = 1; i <= n; ++i)
            cin >> a[i], fa[i] = i;
        map<int, int> mp;
        vector<pair<int, int>> res;
        for (int i = 1; i <= n; ++i) {
            if (mp.count(a[i])) {
                int t = mp[a[i]], v = i;
                res.emplace_back(t, v);
                fa[find(t)] = find(v);
            }
            else
                mp[a[i]] = i;
        }
        for (int i = n - 1 - res.size(); i; --i) {
            for (int j = 0; j < i; ++j)
                b[j].clear();
            for (int j = 1; j <= n; ++j)
                b[a[j] % i].emplace_back(j);
            for (int j = 0; j < i; ++j)
                for (int k = 1; k < b[j].size(); ++k) {
                    int A = b[j][0], B = b[j][k];
                    if (find(A) != find(B)) {
                        fa[find(A)] = find(B);
                        res.emplace_back(A, B);
                        goto ee;
                    }
                }
            ee:;
        }
        reverse(res.begin(), res.end());
        if (res.size() == n - 1) {
            puts("Yes");
            for (auto &[u, v] : res)
                cout << u << ' ' << v << '\n';
        } else {
            puts("NO");
        }
    }
    return 0;
}

E

首先每一次可以挪动一个树的任意一个叶子结点,这样每一次操作树的大小都会减去一。因此一个大小为 \(p\) 的树一定可以给出 \(1\sim p\) 中任意一个整数的贡献。

此时可以发现,若对于两个树满足它们的大小分别为 \(l_1\)\(l_2\),且 \(l_1\le l_2\),那么 \(l_1\) 所对应的树能得到的贡献,\(l_2\) 也一定能得到。

然后考虑贪心。因为是位运算所以考虑按位处理答案。设当前枚举到了第 \(i\) 位。则考虑暴力枚举每一个树,若当前的树的大小超过了 \(2^i\),则这个树可以胜任这个代价。因为还有后面的情况所以考虑尽量贪心的在能胜任的前提下找到树大小最小的树。这个东西可以暴力排序然后去找。因为每一次只更新了一个树的大小,所以排序可以暴力做。

问题是这样贪心有可能并不优秀。例如说只有一个大小为 \(6\) 的树,形如:

pkTdKG8.png

则通过删除任意两个叶子结点操作获得 \(4\) 的贡献之后,若需要找 \(2\) 的贡献,则发现树上没有 \(2\) 的贡献可用。

此时考虑反悔贪心。直接在树上获取 \(4+2=6\) 的贡献(即一次删除一个树),此时需要保证树还足够删除。这样就可以保证贪心始终优秀了。

时间复杂度为 \(O(n\log n)\),具体的细节可以看代码。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1000100;
int a[N];
signed main() {
    srand(time(0));
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    int T;
    cin >> T;
    while (T--) {
        int n;
        cin >> n;
        for (int i = 1; i <= n; ++i) {
            cin >> a[i];
            for (int j = 1; j < a[i]; ++j) {
                int t;
                cin >> t;
            }
        }
        sort(a + 1, a + n + 1);
        int bit = 0;
        for (int i = 20; ~i; --i) {
            int id = -1;
            for (int j = 1; j <= n; ++j)
                if (a[j] >= (1ll << i)) {
                    bit |= (1ll << i);
                    a[j] -= (1ll << i);
                    id = j;
                    break;
                }
            if (~id && id != 1) {
                for (int j = 1; j < id; ++j)
                    if (a[j] >= a[id]) {
                        int t = a[id];
                        for (int k = id; k > j; --k) 
                            a[k] = a[k - 1];
                        a[j] = t;
                        break;
                    }
            }
        }
        cout << bit << '\n';
    }
    return 0;
}

posted @ 2024-07-20 11:07  yhbqwq  阅读(50)  评论(0编辑  收藏  举报