Codeforces Global Round 19 A - E

https://codeforces.com/contest/1637

A

选择一个长度len,将前len个数和后面的数分别排序,问是不是总能把数组排好

  • 显然只有数组之前已经排好序才行
#include 

using namespace std;

typedef long long ll;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        int n;
        cin >> n;
        vector<int> a(n);
        for(auto &i : a) cin >> i;
        if(!is_sorted(a.begin(), a.end())) cout << "YES\n";
        else cout << "NO\n";
    }
    return 0;
}

B

  • 按照给定的规则,问这个数组的所有子数组划分之后的最大值
  • 这题想了好久,想不出来这个最优解是什么,实际上把一个长度为k的数组分成k段总是最优的,没有0的时候这样分分出来是k,使用其他的分法比如说随便分一个,假设说分出来两个区间长度分别为 l , r l, r l,r,这里 l + r = k l+r=k l+r=k,那么这时候答案是 2 + 0 + 0 = 2 ≤ k 2+0+0=2\leq k 2+0+0=2k最多也就分出来 k k k个;即使有0,对于答案的贡献也是全分出来更大,因为 m e x ( 0 ) = 1 mex(0)=1 mex(0)=1,而把0分到别的组里面最大只有 k k k
  • 这题很巧妙,所以说区间 [ l , r ] [l,r] [l,r]对于答案的贡献就是 r − l + 1 + r e s r-l+1+res rl+1+res,其中 r e s res res表示区间0的个数,这样枚举每一个区间时间复杂度是 O ( n 2 ) O(n^2) O(n2)
#include 

using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        int n;
        cin >> n;
        vector<int> a(n);
        for(int i=0;i<n;i++) cin >> a[i];
        long long ans = 0;
        function<long long(int, int)> cal = [&](int l, int r){
            long long res = 0;
            for(int i=l;i<=r;i++){
                if(!a[i]){
                    res += 1;
                }
            }
            return res + r - l + 1;
        };
        for(int i=0;i<n;i++){
            for(int j=i;j<n;j++){
                ans += cal(i, j);
            }
        }
        cout << ans << '\n';
    }
    return 0;
}

C

  • 题读错了,同样想了好久,这里面说了 1 < j < n 1\lt j\lt n 1<j<n,那就简单了,如果是奇数,那么+1即可,然后/2,如果是偶数直接/2,中间如果全是1或者长度是3的前提下中间是奇数那就无解;否则总是有解,因为比如说是1
    3 1 1 1 1,这个3可以产生两个偶数,偶数又可以让奇数变成偶数
#include 

using namespace std;

typedef long long ll;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        int n;
        cin >> n;
        vector<int> a(n);
        for(auto &i : a) cin >> i;
        function<void()> solve = [&](){
            if(n == 3 && (a[1] & 1)){
                cout << -1 << '\n';
                return; 
            }
            ll ans = 0;
            bool ok = false;
            for(int i=1;i<n-1;i++){
                if(a[i] != 1) ok = true;
            }
            if(!ok){
                cout << -1 << '\n';
            }else{
                for(int i=1;i<n-1;i++){
                    if(a[i] & 1){
                        a[i] += 1;
                    }
                    ans += a[i] / 2;
                }
                cout << ans << '\n';
            }
        };
        solve();
    }
    return 0;
}

D

  • 这样一个问题,给你 a [ i ] a[i] a[i] b [ i ] b[i] b[i],现在要最小化 ∑ i = 1 n ∑ j = i + 1 n ( a i + a j ) 2 \sum_{i=1}^{n}\sum_{j=i+1}^{n}(a_i+a_j)^2 i=1nj=i+1n(ai+aj)2 ∑ i = 1 n ∑ j = i + 1 n ( b i + b j ) 2 \sum_{i=1}^{n}\sum_{j=i+1}^{n}(b_i+b_j)^2 i=1nj=i+1n(bi+bj)2的和
  • 平方打开,然后加和,得到 ∑ i = 1 n ∑ j = i + 1 n a i 2 + b i 2 + a j 2 + b j 2 + 2 ( a i a j + b i b j ) \sum_{i=1}^{n}\sum_{j=i+1}^{n}a_i^2+b_i^2+a_j^2+b_j^2+2(a_ia_j+b_ib_j) i=1nj=i+1nai2+bi2+aj2+bj2+2(aiaj+bibj)
  • 前面 ∑ i = 1 n ∑ j = i + 1 n a i 2 + b i 2 + a j 2 + b j 2 \sum_{i=1}^{n}\sum_{j=i+1}^{n}a_i^2+b_i^2+a_j^2+b_j^2 i=1nj=i+1nai2+bi2+aj2+bj2是一个定值,可以 O ( n 2 ) O(n^2) O(n2)得到,
    现在需要处理的问题是最小化 ∑ i = 1 n ∑ j = i + 1 n 2 ( a i a j + b i b j ) \sum_{i=1}^{n}\sum_{j=i+1}^{n}2(a_ia_j+b_ib_j) i=1nj=i+1n2(aiaj+bibj),因为 a i a j a_ia_j aiaj b i b j b_ib_j bibj等价,所以先只考虑
    ∑ i = 1 n ∑ j = i + 1 n a i a j \sum_{i=1}^{n}\sum_{j=i+1}^{n}a_ia_j i=1nj=i+1naiaj
    我们要知道 ∑ i = 1 n ∑ j = i + 1 n a i a j = ∑ i = 1 n ∑ j = 1 n a i a j − ∑ i = 1 n a i a i 2 = ( ∑ i = 1 n a i ) 2 − ∑ i = 1 n a i 2 2 \sum_{i=1}^{n}\sum_{j=i+1}^{n}a_ia_j=\frac{\sum_{i=1}^{n}\sum_{j=1}^{n}a_ia_j-\sum_{i=1}^{n}a_ia_i}{2}=\\\frac{(\sum_{i=1}^{n}a_i)^2-\sum_{i=1}^{n}a_i^2}{2} i=1nj=i+1naiaj=2i=1nj=1naiaji=1naiai=2(i=1nai)2i=1nai2如何证明呢?可以考虑矩阵乘法,左侧相当于一个 n × n n\times n n×n矩阵的一个三角区域(去除主对角线),那么如何得到呢?显然应该使用整个矩阵减去主对角线元素除以2,这也就得到了这个公式
  • 知道了这个公式以后,我们实际上要最小化的式子为 ( ∑ i = 1 n a i ) 2 − ∑ i = 1 n a i 2 + ( ∑ i = 1 n b i ) 2 − ∑ i = 1 n b i 2 = ( ∑ i = 1 n a i ) 2 + ( ∑ i = 1 n b i ) 2 − ∑ i = 1 n a i 2 − ∑ i = 1 n b i 2 (\sum_{i=1}^{n}a_i)^2-\sum_{i=1}^{n}a_i^2+(\sum_{i=1}^{n}b_i)^2-\sum_{i=1}^{n}b_i^2=\\(\sum_{i=1}^{n}a_i)^2+(\sum_{i=1}^{n}b_i)^2-\sum_{i=1}^{n}a_i^2-\sum_{i=1}^{n}b_i^2 (i=1nai)2i=1nai2+(i=1nbi)2i=1nbi2=(i=1nai)2+(i=1nbi)2i=1nai2i=1nbi2
  • 那么其实后面也是一个定值,只需要最小化前面,换句话说,现在有这样一个问题,给你两个数组,可以互换 a [ i ] a[i] a[i] b [ i ] b[i] b[i],求 ( ∑ i = 1 n a i ) 2 + ( ∑ i = 1 n b i ) 2 (\sum_{i=1}^{n}a_i)^2+(\sum_{i=1}^{n}b_i)^2 (i=1nai)2+(i=1nbi)2的最小值
  • 经典dp,设 d p [ i ] [ j ] dp[i][j] dp[i][j]为前 i i i个数能否凑出 j j j,那么问题就转化为01背包了,这样最后得到的就是前 i i i个数能否凑出 j j j,这是对于 a [ i ] a[i] a[i]来说的,也就是 ∑ i = 1 n a i \sum_{i=1}^{n}a_i i=1nai的值,那么根据前缀和 s u m sum sum就可以得到 ∑ i = 1 n b i \sum_{i=1}^{n}b_i i=1nbi ,这样也就求出了这个最小值
#include 

using namespace std;

typedef long long ll;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        int n;
        cin >> n;
        vector<int> a(n + 1), b(n + 1);
        for(int i=1;i<=n;i++) cin >> a[i];
        for(int i=1;i<=n;i++) cin >> b[i];
        int ans = 0;
        int mx = 0;
        for(int i=1;i<=n;i++){
            ans -= a[i] * a[i];
            ans -= b[i] * b[i];
            mx += a[i];
            mx += b[i];
            for(int j=i+1;j<=n;j++){
                ans += a[i] * a[i];
                ans += a[j] * a[j];
                ans += b[i] * b[i];
                ans += b[j] * b[j];
            }
        }
        vector<vector<int> > dp(n + 1, vector<int>(mx + 1));
        dp[0][0] = 1;
        for(int i=1;i<=n;i++){
            for(int j=a[i];j<=mx;j++){
                dp[i][j] |= dp[i - 1][j - a[i]];
            }
            for(int j=b[i];j<=mx;j++){
                dp[i][j] |= dp[i - 1][j - b[i]];
            }
        }
        int res = INT_MAX;
        for(int i=0;i<=mx;i++){
            if(dp[n][i]){
                res = min(res, i * i + (mx - i) * (mx - i));                
            }
        }
        cout << ans + res << '\n';
    }
    return 0;
}

E

  • 定义 c n t x cnt_x cntx x x x出现次数,问 ( c n t x + c n t y ) × ( x + y ) (cnt_x+cnt_y)\times(x+y) (cntx+cnty)×(x+y)的最大值,要求 ( x , y ) (x,y) (x,y)不能是 b a d   p a i r bad\ pair bad pair
  • 一个数组里面所有数的本质不同的出现次数的复杂度是 O ( n ) O(\sqrt n) O(n )的,这个可以考虑出现为1,2,3,4…n,加一起带平方,所以我们可以考虑使用出现次数枚举,注意不能是 b a d   p a i r bad\ pair bad pair,这样的时间复杂度平均为 O ( n n ) O(n\sqrt n) O(nn )
#include 

using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        int n, m;
        cin >> n >> m;
        set<pair<int, int> > s;
        set<int> b;
        map<int, int> mp;
        set<int> a;
        int mx = -1;
        for(int i=0;i<n;i++){
            int x;
            cin >> x;
            a.insert(x);
            mp[x] += 1;
        }
        for(auto i : mp){
            mx = max(mx, i.second);
        }
        vector<vector<int> > g(mx + 1);
        for(auto i : mp){
            g[i.second].push_back(i.first);
            b.insert(i.second);
        }
        for(int i=0;i<=mx;i++) sort(g[i].rbegin(), g[i].rend());
        for(int i=0;i<m;i++){
            int u, v;
            cin >> u >> v;
            if(u > v) swap(u, v);
            s.insert({u, v});
        }
        long long ans = 0;
        for(auto i : a){
            for(auto j : b){
                for(auto k : g[j]){
                    if(i == k) continue;
                    if(!s.count({min(i, k), max(i, k)})){
                        ans = max(ans, 1ll * (i + k) * (mp[i] + j));
                        break;
                    }
                }
            }
        }
        cout << ans << '\n';
    }
    return 0;///
}
  • 这代码昨天能过,今天就不行了,第77个点是从小到大依次排布的,这种暴力会被卡到 O ( n 2 ) O(n^2) O(n2),换成枚举出现次数,这样时间复杂度能回到 O ( A C ) 。 。 。 O(AC)。。。 O(AC)
#include 

using namespace std;

typedef long long ll;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        int n, m;
        cin >> n >> m;
        set<int> a;
        map<int, int> mp;
        for(int i=1;i<=n;i++){
            int x;
            cin >> x;
            a.insert(x);
            mp[x] += 1;
        }
        set<int> b;//本质不同出现次数
        set<pair<int, int> > s;
        for(int i=0;i<m;i++){
            int u, v;
            cin >> u >> v;
            if(u > v) swap(u, v);
            s.insert({u, v});
        }
        int mx = 0;
        for(auto i : mp){
            mx = max(mx, i.second);
        }
        vector<vector<int> > vis(n + 1);
        for(auto i : mp){
            vis[i.second].push_back(i.first);
            b.insert(i.second);
        }
        for(int i=1;i<=n;i++){
            reverse(vis[i].begin(), vis[i].end());
        }
        ll ans = 0;
        for(int cnt_x=1;cnt_x<=n;cnt_x++){
            for(auto x : vis[cnt_x]){
                for(int cnt_y=1;cnt_y<=cnt_x;cnt_y++){
                    for(auto y : vis[cnt_y]){
                        if(x != y && !s.count({min(x, y), max(x, y)})){
                            ans = max(ans, 1ll * (cnt_x + cnt_y) * (x + y));
                            break;
                        }
                    }
                }
            }
        }
        cout << ans << '\n';
    }
    return 0;
}

你可能感兴趣的:(#,题目反思与总结,排序算法,算法,c++)