Codeforces Good bye 2020 A-F题解

Codeforces Good bye 2020 A-F题解

题目链接:https://codeforces.com/contest/1466

A.Bovine Dilemma

题意:给你一些x轴上的点,让你求所给的点和(0, 1)点围成的不同的非零面积的数量。
思路:直接模拟即可。

#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;

int _;
int n;
int x[50];
int vis[100];

int main(){
    
    
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0); 

    cin >> _;
    while(_--){
    
    
        cin >> n;
        memset(vis, 0, sizeof(vis));
        for(int i = 1; i <= n; i++){
    
    
            cin >> x[i];
        }
        for(int i = 1; i <= n; i++){
    
    
            for(int j = i+1 ; j <= n; j++){
    
    
                vis[x[j] - x[i]] = 1;
            }
        }
        int cnt = 0;
        for(int i = 1; i <= 55; i++){
    
    
            if(vis[i])
                cnt++;
        }
        cout << cnt << "\n";
    }
    return 0;
}

B.Last minute enhancements

题意:对于每一个 x i x_i xi我们可以不变或者+1,问最后不同的 x i x_i xi最多有多少个。
思路:贪心,题目中的 x i x_i xi是顺序给出的。我们从后往前贪心构造序列, x n x_n xn = x n x_n xn + 1 +1 +1 x i x_i xi + 1 < +1< +1< x i + 1 x_{i+1} xi+1时,我们让 x i x_i xi+1。这样就能构造出答案序列。

#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;

int _;
int n;
int x[100010];
int cnt[200010];

int main(){
    
    
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin >> _;
    while(_--){
    
    
        cin >> n;
        for(int i = 0; i <= 2*n+3; i++){
    
    
            cnt[i] = 0;
        }
        for(int i = 1; i <= n; i++){
    
    
            cin >> x[i];
        }
        for(int i = n; i >= 1; i--){
    
    
            if(i == n || x[i] + 1 < x[i+1]){
    
    
                x[i]++;
            }
        }
        for(int i = 1; i <= n; i++){
    
    
            cnt[x[i]] = 1;
        }
        int ans = 0;
        for(int i = 1; i <= 2*n+2; i++){
    
    
            if(cnt[i]){
    
    
                ans++;
            }
        }
        cout << ans << "\n";
    }
    
    return 0;
}

C.Canine poetry

题意:给你一个字符串,问你最少替换多少个字符(可以替换成任意字符),使得字符串中不含回文子串。
思路:贪心。简化考虑,你只需要消除长度为2或长度为3的回文子串即可,这样就可以消除全部的回文子串。同时要注意,“abab”这个回文子串要替换2个字符才能不含回文子串。

#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;

int _;
char s[100020];

int main(){
    
    
    cin >> _;
    while(_--){
    
    
        cin >> (s+1);
        int n = strlen(s+1);

        int cnt = 0;
        for(int i = 2; i <= n; i++){
    
    
            if(s[i] == s[i-1] || s[i] == s[i-2]){
    
    
                cnt++;
                s[i] = '#';
            }
        }
        cout << cnt << "\n";
    }
    return 0;
}

D.13th Labour of Heracles

思路:贪心,一个点的度每>1,表示可以多涂一种颜色,对于多涂一种颜色,我们尽可能加上现有点中权值最大的点即可。

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

int _;
int n;
int a[200010];
int b[200010];
int ind[200010];//点的度
int u, v;

bool cmp(const int &a, const int &b){
    
    
    return a > b;
}

int main(){
    
    
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin >> _;
    while(_--){
    
    
        cin >> n;
        long long ans = 0;
        for(int i = 1; i <= n; i++){
    
    
            cin >> a[i];
            ans += 1LL*a[i];
        }
        for(int i = 0; i <= n+1; i++){
    
    
            ind[i] = 0;
            b[i] = 0;
        }
        for(int i = 1; i < n; i++){
    
    
            cin >> u >> v;
            ind[u]++;
            ind[v]++;
        }
        int cnt = 0;
        for(int i = 1; i <= n; i++){
    
    
            ind[i]--;
            while(ind[i]){
    
    
                b[++cnt] = a[i];
                ind[i]--;
            }
        }
        sort(b+1, b+cnt+1, cmp);
        cout << ans << " ";
        for(int i = 1; i <= cnt; i++){
    
    
            ans += 1LL*b[i];
            cout << ans << " ";
        }
        cout << "\n";
    }
    return 0;
}

E.Apollo versus Pan

题意:求 ∑ i = 1 n ∑ j = 1 n ∑ k = 1 n ( x i & x j ) ( x j ∣ x k ) \sum_{i=1}^n{\sum_{j=1}^n{\sum_{k=1}^n{(x_i\&x_j)(x_j|x_k)}}} i=1nj=1nk=1n(xi&xj)(xjxk)
思路:推公式。我们可以考虑交换求和次序。
∑ i = 1 n ∑ j = 1 n ∑ k = 1 n ( x i & x j ) ( x j ∣ x k ) = ∑ j = 1 n [ ∑ i = 1 n ( x i & x j ) ] [ ∑ k = 1 n ( x j ∣ x k ) ] \begin{aligned} \sum_{i=1}^n{\sum_{j=1}^n{\sum_{k=1}^n{(x_i\&x_j)(x_j|x_k)}}} &= \sum_{j=1}^n[\sum_{i=1}^n{(x_i\&x_j)}][\sum_{k=1}^n(x_j|x_k)]\\ \end{aligned} i=1nj=1nk=1n(xi&xj)(xjxk)=j=1n[i=1n(xi&xj)][k=1n(xjxk)]
我们枚举 x j x_j xj,每一次求出 ∑ i = 1 n ( x i & x j ) \sum_{i=1}^n{(x_i\&x_j)} i=1n(xi&xj) ∑ k = 1 n ( x j ∣ x k ) \sum_{k=1}^n(x_j|x_k) k=1n(xjxk)即可。
我们考虑位运算,拆位进行运算。
我们定义 f ( x , c ) f(x, c) f(x,c)表示 x x x的第 c c c位。
比如 f ( 7 , 0 ) = 1 , 7 2 = 111 f(7, 0) = 1, 7_2 = 111 f(7,0)=1,72=111
∑ i = 1 n ( x i & x j ) = ∑ c = 0 M 2 c ∑ i f ( x i , c ) f ( x j , c ) \sum_{i=1}^n{(x_i\&x_j)} = \sum_{c = 0}^M{2^c\sum_i{f(x_i, c)f(x_j, c)}} i=1n(xi&xj)=c=0M2cif(xi,c)f(xj,c)
∑ k = 1 n ( x j ∣ x k ) = ∑ c = 0 M 2 c ∑ k [ 1 − ( 1 − f ( x j , c ) ) ( 1 − f ( x k , c ) ) ] \sum_{k=1}^n(x_j|x_k) = \sum_{c = 0}^M{2^c\sum_k[1 - (1 - f(x_j, c))(1-f(x_k, c))]} k=1n(xjxk)=c=0M2ck[1(1f(xj,c))(1f(xk,c))]
∑ k = 1 n ( x j ∣ x k ) = ∑ c = 0 M 2 c [ n − ( 1 − f ( x j , c ) ) ∑ k ( 1 − f ( x k , c ) ) ] \sum_{k=1}^n(x_j|x_k) = \sum_{c = 0}^M{2^c[n - (1 - f(x_j, c))\sum_k(1 - f(x_k, c))]} k=1n(xjxk)=c=0M2c[n(1f(xj,c))k(1f(xk,c))]
我们每一次枚举 x j x_j xj,同时预处理出来 ∑ i = 1 n x i \sum_{i = 1}^n{x_i} i=1nxi的每一位有多少个1,就可以在O(nM)求出答案,题目中说明了M < 60。

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

int _;
int n;
long long x[500010];//数组
int cnt[64];//拆位存储
const long long MOD = 1e9+7;
const int K = 60;//最高数位

int main(){
    
    
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> _;
    while(_--){
    
    
        cin >> n;
        memset(cnt, 0, sizeof cnt);
        for(int i = 1; i <= n; i++){
    
    
            cin >> x[i];
            for(int j = 0; j < K; j++){
    
    
                if(x[i] >> j & 1){
    
    
                    cnt[j]++;
                }
            }
        }

        long long ans = 0;
        for(int i = 1; i <= n; i++){
    
    
            long long ans_and = 0;
            long long ans_or = 0;
            for(int j = 0; j < K; j++){
    
    
                if(x[i] >> j & 1){
    
    
                    ans_and = (ans_and + (1LL << j)%MOD*cnt[j]%MOD)%MOD;
                    ans_or = (ans_or + (1LL << j)%MOD*n%MOD)%MOD;
                }
                else{
    
    
                    ans_or = (ans_or + (1LL << j)%MOD*cnt[j]%MOD)%MOD;
                }
            }
            ans = (ans + ans_and*ans_or%MOD)%MOD;
        }
        cout << ans << "\n";
    }
    return 0;
}

F.Euclid’s nightmare

题意: Z 2 Z_2 Z2的定义是一个模2加法。给你一些向量,这些向量有一个闭包,求闭包的最小覆盖。
思路:并查集。一个向量最多两维是1,我们可以考虑并查集。对于一个向量,如果这个向量中为1的两维没有在一个连通分量里面,表示这个向量是最小覆盖中的向量,并把这个向量中为1的两维连在一起。同时,如果这个向量的两维已经在一个连通分量里面了,表示这个向量可以有之前的不同向量相加而成。有的向量只有1维为1,我们对这个向量增加一维,进行增广,让这个向量的第m+1维为1,就可以用并查集了。

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

int n, m;
int fa[500010];
const long long MOD = 1e9+7;

int find(int x){
    
    
    if(x == fa[x])
        return x;
    return fa[x] = find(fa[x]);    
}

bool Union(int x, int y){
    
    
    int x1 = find(x);
    int y1 = find(y);
    if(x1 != y1){
    
    
        fa[x1] = y1;
        return true;
    }
    return false;
}

int main(){
    
    
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    //一个数不能连并查集,要增广出m+1
    cin >> n >> m;
    for(int i = 1; i <= m+1; i++){
    
    
        fa[i] = i;
    }
    vector<int> ans;
    for(int i = 1; i <= n; i++){
    
    
        int b = m+1;
        int k, a;
        cin >> k;
        cin >> a;
        if(k > 1)
            cin >> b;
        if(Union(a, b))
            ans.push_back(i);
    }
    long long res = 1;
    for(int i = 1; i <= ans.size(); i++){
    
    
        res = res*2%MOD;
    }
    cout << res << " " << ans.size() << "\n";
    for(auto i : ans){
    
    
        cout << i << " ";
    }
    cout << "\n";
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Einsteinme/article/details/112055201