Codeforces Round #769 (Div. 2) A -D

Posted by vMan on Thu, 03 Feb 2022 07:28:24 +0100

https://codeforces.com/contest/1632

A

  • If there is a palindrome substring in a string whose length is greater than 1, NO is output; otherwise, YES is output
  • Violence can be done, but there is a better idea. Note that this is a 01 string. If the length is greater than 2, there must be such a palindrome string, because both 001 and 010 meet this nature; If the length is 1, then it is YES; If the length is 2, judge whether the first two characters are the same
#include <bits/stdc++.h>

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;
        string s;
        cin >> s;
        if(n == 1 || (n == 2 && s[0] != s[1])) cout << "YES\n";
        else cout << "NO\n";
    }
    return 0;
}

B

  • Random arrangement [ 0 , n − 1 ] [0,n-1] [0,n − 1], find the minimum value of the XOR maximum of the two adjacent terms inside
  • Consider the highest bit 1, because this bit must match a position where the highest bit is 0, and the value from XOR is at least the number corresponding to the highest bit binary. How can this number be set as this minimum value? Obviously, you only need to put a 0 in front of this number, and the order of other positions remains unchanged, because the highest pairwise exclusive or before this number must be 0, and the highest pairwise exclusive or after this number must also be 0, which is not as big as this position, so this is the smallest of the largest
#include <bits/stdc++.h>

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;
        for(int i=1;i<n;i++){
            if(i == (1 << (31 - __builtin_clz(n - 1)))) cout << 0 << ' ';
            cout << i << " \n"[i == n - 1];
        }
    }
    return 0;
}

C

Two numbers a , b a,b a. B. There are three operations, a = a + 1 , b = b + 1 , a = a ∣ b a=a+1,b=b+1,a=a|b a=a+1,b=b+1,a=a ∣ b, you can choose one of them. Ask how many operations can make a = b a=b a=b

  • in consideration of a ∣ b ≥ m a x ( a , b ) a|b\geq max(a,b) A ∣ B ≥ max(a,b), so this operation must be carried out last and can only be carried out once at most, so now consider how to carry out the addition operation
  • The maximum number of operations is b − a b-a b−a
  • Consider within these times, keep right a , b a,b a. B add separately, and update the answer if the goal is reached
#include <bits/stdc++.h>

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 a, b;
        cin >> a >> b;
        int ans = b - a;
        for(int i=a;i<=a+ans;i++){
            if(i == b){
                ans = min(ans, i - a);
            }else if((i | b) == b){
                ans = min(ans, i - a + 1);
            }
        }
        for(int i=b;i<=b+ans;i++){
            if((i | a) == i){
                ans = min(ans, i - b + 1);
            }
        }
        cout << ans << '\n';
    }
    return 0;
}
  • Looking at the tourist code and live video, I found that his method is very clever. He uses one n e w _ a new\_a new_a. Add its last binary 1 every time, so it keeps approaching b b b. Take the minimum value between them
#include <bits/stdc++.h>

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 a, b;
        cin >> a >> b;
        int ans = b - a;
        int new_a = a;
        while(new_a <= b){
            int x = new_a - a;
            for(int y=0;y<=b-a;y++){
                ans = min(ans, x + y + 1 + ((x + a) | (y + b)) - (y + b));
                //x is how much a needs to be replenished each time, y is how much b needs to be replenished each time, 1 is XOR, and finally how much is the difference between a and b after these operations
            }
            new_a += (new_a & (-new_a));
        }
        cout << ans << '\n';
    }
    return 0;
}

D

If there is an interval g c d gcd gcd is equal to the length of the interval, then this interval will annoy the audience. Ask how many elements need to be changed at least for all prefixes of this array, so that each non empty prefix will not annoy the audience

  • An intuitive idea is that we enumerate each left endpoint, and then find the position farthest from it that satisfies that the interval gcd is equal to the length of the interval. There can only be one such position, and then we can turn this number into a large prime number, because we know that the later the prefix gcd will be smaller, and the prefix gcd can have a maximum of log, because it needs to be divided by 2 at least every time
  • Then we enumerate each left endpoint, then divide the right endpoint into two to get each segment, and then process it from left to right in turn
  • Using segment trees or s t st st table interval g c d gcd Quick acquisition of gcd
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin >> n;
    vector<int> a(n);
    vector<vector<int> > ST(n, vector<int>(30));
    for(int i=0;i<n;i++){
        cin >> a[i];
        ST[i][0] = a[i];
    }
    for(int j=1;j<30;j++){
        for(int i=0;i+(1<<(j - 1))<n;i++){
            ST[i][j] = __gcd(ST[i][j - 1], ST[i + (1 << (j - 1))][j - 1]);
        }
    }
    vector<pair<int, int> > seg;
    function<int(int, int)> Get = [&](int l, int r){
        int sz = r - l + 1;
        int t = 31 - __builtin_clz(sz);
        return __gcd(ST[l][t], ST[r - (1 << t) + 1][t]);
    };
    for(int i=0;i<n;i++){
        int l = i;
        int r = n - 1;
        while(l <= r){
            int mid = ((r - l) >> 1) + l;
            if(Get(i, mid) >= mid - i + 1){
                l = mid + 1;
            }else{
                r = mid - 1;
            }
        }
        if(Get(i, l - 1) == l - 1 - i + 1) seg.push_back(make_pair(i, l - 1));
    }
    vector<int> ans(n + 1);
    int covered = -1;
    for(auto &i : seg){
        if(i.first > covered){
            covered = i.second;
            ans[i.second] = 1;
        }
    }
    for(int i=1;i<n;i++){
        ans[i] += ans[i - 1];
    }
    for(int i=0;i<n;i++){
        cout << ans[i] << " \n"[i == n - 1];
    }
    return 0;
}
  • The key to this problem is that we need to know that the prefix gcd satisfies a [ i − 1 ] ≥ a [ i ] a[i-1]\geq a[i] a[i − 1] ≥ a[i], so when enumerating the left endpoint and the right endpoint, because this value is getting smaller and smaller, there will be no value smaller than it. If the right endpoint of the first point is changed, then all the numbers between them need not be looked at, because there can be no qualified interval
  • The tour solution is still so elegant. He uses a tuple to record the suffix gcd of all positions, because there are only log at most whether it is the prefix gcd or the suffix gcd. Then we record all the suffixes gcd of the current position and the corresponding start and end positions. If a suffix is found to meet the conditions, the right endpoint will be updated to a large prime number, At the same time, you don't need to look at the left. The reason is the same as above. This method is very clever and doesn't need any data structure
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin >> n;
    vector<int> a(n);
    for(int i=0;i<n;i++) cin >> a[i];
    int ans = 0;
    int covered = -1;
    vector<array<int, 3>> s;
    for(int i=0;i<n;i++){
        for(auto &t : s){
            t[2] = __gcd(t[2], a[i]);
        }
        s.push_back({i, i, a[i]});
        int cnt = 0;
        for(int j=0;j<(int)s.size();j++){
            if(cnt > 0 && s[j][2] == s[cnt - 1][2]){
                s[cnt - 1][1] = s[j][1];
            }else{
                s[cnt++] = s[j];
            }
        }
        s.resize(cnt);
        for(auto &t : s){
            int at = i - t[2] + 1;//Left endpoint
            if(t[0] <= at && at <= t[1]){
                if(at > covered){
                    covered = i;
                    ans += 1;
                }
            }
        }
        cout << ans << " \n"[i == n - 1];
    }
    return 0;
}

Topics: Algorithm Dynamic Programming linear algebra