Educational Codeforces Round 23

A. Treasure Hunt

#include <bits/stdc++.h>

using namespace std;

int read() {
    int x = 0, f = 1, ch = getchar();
    while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
    if (ch == '-') f = -1, ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}

int main(){
    int a , b , c , d , x , y;
    cin >> a >> b >> c >> d >> x >> y;
    a = abs( a - c ) , b = abs( b - d );
    if( a % x == 0 && b % y == 0 && (a / x)%2 == (b / y)%2 )
        cout << "YES\n";
    else
        cout << "NO\n";
    return 0;
}

B. Makes And The Product

排个序,然后取最小的三个数,然后分类讨论一下就好了。

#include <bits/stdc++.h>

using namespace std;

#define int long long
int read() {
    int x = 0, f = 1, ch = getchar();
    while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
    if (ch == '-') f = -1, ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}

int32_t main() {
    int n = read();
    vector<int> a(n);
    map<int, int> cnt;

    for (auto &i: a)
        i = read(), cnt[i]++;

    sort(a.begin(), a.end());

    int x = a[0], y = a[1], z = a[2];
    if (x == y && y == z) {
        int m = cnt[x];
        cout << m * (m - 1) * (m - 2) / 6;
    } else if (x == y && y != z) {
        int p = cnt[x], q = cnt[z];
        cout << p * (p - 1) / 2 * q;
    } else if (x != y && y == z) {
        int p = cnt[x], q = cnt[z];
        cout << p * q * (q - 1) / 2;
    } else
        cout << cnt[x] * cnt[y] * cnt[z];

    return 0;
}

C. Really Big Numbers

定义\(x\)每一位的和是\(f(x)\),则求在\([1,n]\)\(x - f(x) \ge s\)的个数。

首先如果\(a\)满足\(a-f(a)\ge s\),则\(a+1 - f(a+1) >= s\)一定满足。所以我们直接二分的找到最小的\(x\)即可

#include <bits/stdc++.h>

using namespace std;

#define int long long

int read() {
    int x = 0, f = 1, ch = getchar();
    while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
    if (ch == '-') f = -1, ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}

int32_t main() {
    auto f = [](int x){
        int ans = 0;
        while( x ) ans += x % 10 , x /= 10;
        return ans;
    };

    int n , s;
    cin >> n >> s;
    int l = 1 , r = n , mid , ans = -1;
    while( l <= r ){
        mid = ( l + r ) >> 1;
        if( mid - f(mid) >= s ) ans = mid , r = mid - 1;
        else l = mid + 1;
    }
    if( ans == -1 ) cout << 0 << "\n";
    else cout << n - ans + 1;
    return 0;
}

D. Imbalanced Array

这题最朴素的想法是枚举区间,然后统计最值,复杂度\(O(N^3)\).

很容易想到用ST表优化到\(O(N^2)\).

这样的思路其实是枚举区间,我们其实可以换一个角度去思考,对于\(a_i\)在哪些区间里面做最值。

以最大值为例,如果我们找到\(i\)做侧第一个比\(a_i\)大的值\(a_l\)和右侧的第一个\(a_r\)其实就是\([l+1,r-1]\)之间左右包含\(i\)的区间的最大值都是\(a_i\).

这样的话,用 ST表配合二分求出左右共四个端点即可,复杂度\(O(N\log N)\).

这里求最值的话也可利用单调栈的性质优化到\(O(N)\).

#include <bits/stdc++.h>

using namespace std;

#define int long long

int read() {
    int x = 0, f = 1, ch = getchar();
    while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
    if (ch == '-') f = -1, ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}

int32_t main() {
    int n = read();
    vector<int> a(n + 2);
    vector<int> maxL(n + 1), maxR(n + 1), minL(n + 1), minR(n + 1);

    for (int i = 1; i <= n; i++) a[i] = read();
    a[0] = a[n + 1] = LLONG_MAX;

    // 左侧最大
    stack<int> s;
    s.push(0);
    for (int i = 1; i <= n; i++) {
        while (a[i] >= a[s.top()]) s.pop();
        maxL[i] = i - s.top(), s.push(i);
    }
    // 右侧最大
    s = stack<int>(), s.push(n + 1);
    for (int i = n; i >= 1; i--) {
        while (a[i] > a[s.top()]) s.pop();
        maxR[i] = s.top() - i, s.push(i);
    }
    // 左侧最小
    a[0] = a[n + 1] = LLONG_MIN;
    s = stack<int>(), s.push(0);
    for (int i = 1; i <= n; i++) {
        while (a[i] <= a[s.top()]) s.pop();
        minL[i] = i - s.top(), s.push(i);
    }
    // 右侧最小
    s = stack<int>(), s.push(n + 1);
    for (int i = n; i >= 1; i--) {
        while (a[i] < a[s.top()]) s.pop();
        minR[i] = s.top() - i, s.push(i);
    }

    int res = 0;
    for (int i = 1; i <= n; i++)
        res += (maxL[i] * maxR[i] - minL[i] * minR[i]) * a[i];
    printf("%lld\n", res);
    return 0;
}

E. Choosing The Commander

用01Tire维护当前集合中的数,注意在插入的时候从二进制的高位开始插入。

对于查询操作,我们沿着\(p_i \oplus l_i\)的路径去遍历前缀,如果对于当前的节点向另一个方向走可以使得\(p_x\oplus p_i < l_i\),就加上另一个子树的权值即可。

#include <bits/stdc++.h>

using namespace std;

typedef bitset<30> Num;

class Tire {
private:
    struct Node {
        int v, cnt, size, to0, to1;

        Node(int v = -1, int cnt = 0, int to0 = -1, int to1 = -1) : v(v), cnt(cnt), to0(to0), to1(to1) {
            size = 0;
        };
    };

    vector<Node> T;
public:
    Tire() {
        T.push_back(Node());
    }

    void insert(Num x) {
        vector<int> road;
        int pos = 0;
        for (int i = 29; i >= 0; i--) {
            if (x[i] == 0) {
                if (T[pos].to0 == -1) T[pos].to0 = T.size(), T.emplace_back(0);
                pos = T[pos].to0;
            } else {
                if (T[pos].to1 == -1) T[pos].to1 = T.size(), T.emplace_back(1);
                pos = T[pos].to1;
            }
            road.push_back(pos);
        }
        T[pos].cnt++;
        for (auto i: road) T[i].size++;
    }

    void del(Num x) {
        vector<int> road;
        int pos = 0;
        for (int i = 29; i >= 0; i--) {
            if (x[i] == 0) pos = T[pos].to0;
            else pos = T[pos].to1;
            road.push_back(pos);
        }
        T[pos].cnt--;
        for (auto i: road) T[i].size--;
    }

    int query(Num x, Num y) {
        int ans = 0, pos = 0;
        for (int i = 29, p; i >= 0; i--) {
            if (y[i] == 1) {
                if (x[i] == 0) p = T[pos].to0;
                else p = T[pos].to1;
                if (p != -1) ans += T[p].size;
            }
            if (x[i] ^ y[i] == 0) pos = T[pos].to0;
            else pos = T[pos].to1;
            if (pos == -1) break;
        }
        return ans;
    }

};

int main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int q;
    cin >> q;
    Tire tire;
    for (int op, p, l; q; q--) {
        cin >> op >> p;
        if (op == 1) tire.insert(Num(p));
        else if (op == 2)tire.del(Num(p));
        else {
            cin >> l;
            cout << tire.query(Num(p), Num(l)) << "\n";
        }
    }
    return 0;
}

F. MEX Queries

把这个题当做有一个大小为\(10^{18}\)的数组,数组一开始全为 0,有三种操作,区间赋1、区间赋 0、区间取反,每次操作后询问最靠左的 0 的下标。

这样就会发现这是 ODT 的模板题,mex就是第一个值为 0 的区间的左端点。

#include <bits/stdc++.h>

using namespace std;

#define int long long

int read() {
    int x = 0, f = 1, ch = getchar();
    while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
    if (ch == '-') f = -1, ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}

class ODT {
private:
    struct Node {
        int l, r;
        mutable int val;

        Node(int l, int r = 0, int val = 0) : l(l), r(r), val(val) {};

        bool operator<(Node b) const {
            return l < b.l;
        }

        int len() const {
            return r - l + 1;
        }
    };

    const int len = 1e18;
    set<Node> s;
public:
    ODT() {
        s.insert(Node(1, len, 0));
    }

    auto split(int x) {
        if (x > len) return s.end();
        auto it = --s.upper_bound(Node(x));
        if (it->l == x) return it;
        int l = it->l, r = it->r, v = it->val;
        s.erase(it);
        s.insert(Node(l, x - 1, v));
        return s.insert(Node(x, r, v)).first;
    }

    void assign(int l, int r, int v) {
        auto itr = split(r + 1), itl = split(l);
        s.erase(itl, itr);
        s.insert(Node(l, r, v));
    }

    void invert(int l, int r) {
        auto itr = split(r + 1), itl = split(l);
        for (auto it = itl; it != itr; it++)
            it->val ^= 1;
    }

    int mex() {
        for (auto it: s) {
            if (it.val == 0) return it.l;
        }
        return len + 1;
    }
};

int32_t main() {
    int q = read();
    ODT odt;
    for( int op , l , r ; q ; q -- ){
        op = read() , l = read() , r = read() ;
        if( op == 1 )
            odt.assign( l , r , 1 );
        else if( op == 2 )
            odt.assign( l , r , 0 );
        else odt.invert( l , r );
        printf("%lld\n" , odt.mex() );
    }
    return 0;
}
posted @ 2023-07-11 15:49  PHarr  阅读(11)  评论(0编辑  收藏  举报