Codeforces Round 1062 (Div.4) 题解

写在开头

蒟蒻第一次写题解有不足请多多指出多多原谅呀!!!

A

题面

给定4个数,判断是否能构成正方形。

解析

只需判断4个数是否相等即可。

代码

#include <bits/stdc++.h>
using namespace std;

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        int a, b, c, d;
        cin >> a >> b >> c >> d;
        if(a == b && b == c && c == d) cout << "YES\n";
        else cout << "NO\n";
    }
    return 0;
}

B

题面

给定两个长度相等字符串,判断这两个字符串能否按照一定顺序重排之后相等。

解析

两个字符串长度相等,判断每个字母出现次数是否一致即可。

代码

#include <bits/stdc++.h>
using namespace std;

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        int n;
        cin >> n;
        string s1, s2;
        cin >> s1 >> s2;
        vector <int> a(26, 0), b(26, 0);
        for(int i = 0; i < n; i++){
            a[s1[i] - 'a']++;
        }
        for(int i = 0; i < n; i++){
            b[s2[i] - 'a']++;
        }
        int tag = 1;
        for(int i = 0; i < 26; i++){
            if(a[i] != b[i]){
                tag = 0;
                break;
            }
        }
        cout << (tag ? "YES\n" : "NO\n");
    }
    return 0;
}

C

题面

给定一个长为 \(n\) 的数组 \(a\) ,两个元素 \(a_{i}\)\(a_{j}\) 可交换位置当且仅当 \(a_{i}\)\(a_{j}\) 奇偶性不同,不限制交换次数,问能得到的数组的最小字典序是多少。

解析

关键点是注意到如果数组中奇数和偶数都有那么整个数组可以以任意顺序排列。

证明如下:

\(n \le 2\) 时是平凡的,考虑 \(n > 2\),那么存在 \(i\)\(j\)\(k\) 使得 \(a_{i} \equiv a_{j} \mod 2\) 并且 \(a_{i} \not\equiv a_{k} \mod {2}\),那么我们可以按照 \((a_{i}, a_{k})(a_{j}, a_{k})(a_{i}, a_{k})\) 的顺序交换,从而实现交换两个奇偶性相同的元素。\(\square\)

从而,如果数组所有数的奇偶性都相同那么答案就是原数组,否则就是从小到大排序后的数组。

代码

#include <bits/stdc++.h>
using namespace std;

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        int n;
        cin >> n;
        vector <int> v(n);
        multiset <int> s[2];   //这里用一个bool判断即可,没必要用set
        for(int i = 0; i < n; i++){
            cin >> v[i];
            s[v[i] & 1].insert(v[i]);
        }
        if(s[0].empty() || s[1].empty()){
            for(int i = 0; i < n; i++){
                cout << v[i] << ' ';
            }
        }else{
            sort(v.begin(), v.end());
            for(int i = 0; i < n; i++){
                cout << v[i] << ' ';
            }
        }
        cout << '\n';
    }
    return 0;
}

D

题面

给定 \(n\) 个数,求一个最小的 \(x\),使得存在一个数与 \(x\) 互素,其中 \(2 \le x \le 10^{18}\),否则返回 \(-1\)

解析

如果 \(x\) 存在,那它一定是一个素数,否则 \(x\) 存在一个素因子 \(p\),使得 \(p\) 也和找到的数互素,并且 \(p\) 更小,所以我们只用判断素数即可。

注意到前 \(16\) 个素数的乘积就已经大于 \(10^{18}\) 了,从而对一个数 \(y\) 来说,如果存在 \(x\) 使得他们互素,那前 \(16\) 个素数中一定有一个不是 \(y\) 的因数,循环判断找最小即可。

代码

#include <bits/stdc++.h>
using namespace std;
#define ll long long

int a[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59};

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        int n;
        cin >> n;
        vector <ll> v(n);
        for(int i = 0; i < n; i++) cin >> v[i];
        int tag = 1e9;
        for(int i = 0; i < n; i++){
            for(int j = 0; j < 17; j++){
                if(v[i] % a[j] != 0){
                    tag = min(a[j], tag);
                }
            }
        }
        END:;
        cout << (tag != 1e9 ? tag : -1) << '\n';
    }
    return 0;
}

E

题面

给定一个大小为 \(n\) 的数组 \(a\),其中所有的数都在区间 \([0,x]\) 内,找在这个区间内找 \(k\) 个数,使得数组中的数到这 \(k\) 个数的最小距离最大。

解析

暴力即可。首先将数组 \(a\) 排序,对 \([0, a_{1}, a_{2}, ..., a_{n}, x]\)中每一个区间找到距离区间两端最远的点,将其放入优先队列维护,每次取出一个点后将两边的点再压入优先队列中,取 \(k\) 个即可。

代码

#include <bits/stdc++.h>
using namespace std;

struct pii{
    int len, pos;
    int tag;

    bool operator< (const pii x) const{
        return len < x.len;
    }
};

void solve(){
    int n, k, x;
    cin >> n >> k >> x;
    vector <int> a(n + 1, 0);
    for(int i = 1; i <= n; i++) cin >> a[i];
    sort(a.begin() + 1, a.end());
    priority_queue <pii> q;
    set <int> s;
    q.push({a[1], 0, 1});
    q.push({x - a[n], x, -1});
    for(int i = 1; i < n; i++){
        int len = a[i + 1] - a[i];
        if(len & 1){
            q.push({len / 2, a[i] + len / 2, -1});
            q.push({len / 2, a[i] + len / 2 + 1, 1});
        }else{
            q.push({len / 2, a[i] + len / 2, 0});
        }
    }
    int num = 0;
    while(num < k){
        auto f = q.top();
        q.pop();
        if(s.find(f.pos) == s.end()){
            cout << f.pos << ' ';    
            num++;        
            s.insert(f.pos);
        }
        if(f.tag && f.pos + f.tag >= 0 && f.pos + f.tag <= x) q.push({f.len - 1, f.pos + f.tag, f.tag});
        else if(!f.tag){
            if(f.pos + 1 >= 0 && f.pos + 1 <= x) q.push({f.len - 1, f.pos + 1, 1});
            if(f.pos - 1 >= 0 && f.pos - 1 <= x) q.push({f.len - 1, f.pos - 1, -1});
        }
    }
    cout << '\n';
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while(t--) solve();
    return 0;
}

F

题面

给定一棵树,对于每个节点作为根,计算所有由个不同节点组成的集合的最近公共祖先(LCA)的不同节点数 \(S_{i}\),最终求所有根节点的和 \(\sum{S_{i}}\)

解析

考虑一个点什么时候会成为 \(k\) 个点的 \(lca\) ,这当且仅当这个点的子树不小于 \(k\)

我们首先以 \(1\) 为根建树,对一个节点 \(u\),考虑有多少其他节点为根节点时 \(u\) 会对答案有贡献。

如果 \(u\) 的子树大小不小于 \(k\),那除了 \(u\) 的子树上的节点,剩下的节点当作根节点时 \(u\) 的子树不变,此时贡献为 \(n - siz_{u}\)

再考虑 \(u\) 的子树上的节点 \(v\) 为根节点,设 \(w\)\(u\) 的儿子且 \(w\)\(v\) 的祖先,那么这时 \(u\) 的新子树为除了 \(w\) 的分支的其余节点,也就是说如果一个分支满足 \(siz_{w} \le n - k\),那么u的新子树大小不小于 \(k\),从而这个分支 \(w\) 上的所有节点都会产生贡献,为 \(siz_{w}\)

代码

#include <bits/stdc++.h>
using namespace std;
#define ll long long

void solve(){
    int n, k;
    cin >> n >> k;
    vector <vector <int>> g(n + 1);
    for(int i = 1; i < n; i++){
        int x, y;
        cin >> x >> y;
        g[x].push_back(y);
        g[y].push_back(x);
    }
    vector <int> siz(n + 1, 0);
    ll ans = 0;
    if(n == k){
        cout << n << '\n';
        return;
    }
    function <void(int)> dfs = [&](int u){
        siz[u] = 1;
        for(auto v : g[u]){
            if(!siz[v]){
                dfs(v);
                siz[u] += siz[v];
                if(siz[v] <= n - k) ans += siz[v];
            }
        }
        if(siz[u] >= k) ans += n - siz[u];
        ans++;
    };
    dfs(1);
    cout << ans << '\n';
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while(t--) solve();
    return 0;
}

G

题面

给定一个长为 \(n\) 的数组,可以将一个个元素 \(a_i\) 修改为任意值,代价为 \(w_i\),求把这个数组变为不减的最小代价。

解析

考虑一个方案中没有被修改的元素,他们一定构成一个不减的子序列,要让修改的代价最小,只需让这些没有修改的代价最大。

考虑 \(dp\)\(dp_i\) 表示将前i个元素组成的序列变为不减的,其中没有被修改的元素的代价的最大值(第 \(i\) 个元素也不修改),那么有状态转移方程:

\[dp_i = \max(w_i + dp_j),j < i, a_j < a_i \]

使用的时候在最后加一个代价为 \(0\),数值无穷大的元素即可。

使用树状数组维护 \(dp\) 最大值,时间复杂度 \(O(n\log{n})\)

代码

#include <bits/stdc++.h>
using namespace std;
#define ll long long

template <class T>
struct Fenwick{
    int n;
    vector <T> tr;

    Fenwick(int _n){
        init(_n);
    }

    void init(int _n){
        n = _n;
        tr.assign(n + 1, T{});
    }

    int lowbit(int x){return x & -x;}

    void add(int x, const T &w){
        for(int i = x; i <= n; i += lowbit(i)){
            tr[i] = tr[i] + w;
        }
    }

    T query(int x){
        T ans{};
        for(int i = x; i > 0; i -= lowbit(i)){
            ans = ans + tr[i];
        }
        return ans;
    }

    T sum(int x, int y){
        return query(y) - query(x - 1);
    }
};

struct i64{
    ll x;

    i64 operator+ (i64 a){
        return {max(x, a.x)};
    }
};

void solve(){
    int n;
    cin >> n;
    vector <int> a(n + 1, 0);
    vector <ll> w(n + 1, 0), dp(n + 1, 0);
    for(int i = 1; i <= n; i++) cin >> a[i];
    for(int i = 1; i <= n; i++) cin >> w[i];
    auto b = a;
    sort(b.begin(), b.end());
    b.erase(unique(b.begin(), b.end()), b.end());
    Fenwick <i64> t(n);
    ll sum = 0;
    for(int i = 1; i <= n; i++){
        sum += w[i];
        auto it = lower_bound(b.begin(), b.end(), a[i]);
        dp[i] = w[i] + t.query(it - b.begin()).x;
        t.add(it - b.begin(), {dp[i]});
    }
    cout << sum - t.query(b.size() - 1).x << '\n';
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while(t--) solve();
    return 0;
}

完结撒花

posted @ 2025-10-29 18:24  x1a0qiya  阅读(41)  评论(0)    收藏  举报