Educational Codeforces Round 23

Educational Codeforces Round 23

https://codeforces.com/contest/817
数据结构专场。

A. Treasure Hunt

#include <bits/stdc++.h>

using namespace std;

int main () {
    int x1, x2, y1, y2, x, y;
    cin >> x1 >> y1 >> x2 >> y2 >> x >> y;
    int dx = abs(x2 - x1), dy = abs(y2 - y1);
    if ((dx % x) || (dy % y)) {
        cout << "NO";
        return 0;
    }
    int cntx = dx / x, cnty = dy / y;
    //cout << cntx << ' ' << cnty << endl;
    if ((cntx % 2) != (cnty % 2))   cout << "NO";
    else    cout << "YES";
}

B. Makes And The Product

分类讨论没想清楚。

#include <bits/stdc++.h>
#define ll long long

using namespace std;
const int N = 1e5 + 5;
int a[N], n;

int main () {
    cin >> n;
    map<int, int> mp;
    for (int i = 1; i <= n; i++)    cin >> a[i], mp[a[i]] ++;
    sort (a + 1, a + n + 1);
    ll ans = 0;
    if (a[3] == a[1])    ans = (1ll * mp[a[1]] * (mp[a[1]] - 1) * (mp[a[1]] - 2)) / 6;
    else if (a[2] == a[3])  ans = (1ll * mp[a[2]] * (mp[a[2]] - 1)) / 2;
    else    ans = mp[a[3]];
    cout << ans << endl;
}

//第三小的数字有多少个

C. Really Big Numbers

打表发现单调性之后直接二分。
此题因为过于重视研究规律而没有往算法的方向想emmm很可惜

// LUOGU_RID: 102953585
#include <bits/stdc++.h>
#define ll long long

using namespace std;

ll cal (ll x) { //打表
    string s = to_string (x);
    ll sum = 0;
    for (int i = 0; i < s.size (); i++) sum += (s[i] - '0');
    return abs (x - sum);
}

int main () {
    ll n, s;
    cin >> n >> s;
    // for (ll i = 0; i <= 10000; i += 10) {
    //     ll dx = cal (i);
    //     cout << i << ' ' << dx  << endl;
    // }
    //根据s倒推数字
    ll l = 0, r = n + 1;
    while (l < r) {
        ll mid = l + r >> 1;
        if (cal (mid) < s)  l = mid + 1;
        else    r = mid;
    }
    cout << max (0ll, n - r + 1) << endl;
}

//逢10进9, 逢100进18, 逢1000进27, ... , 
//打表出单调性之后直接二分

D. Imbalanced Array

四颗单调栈维护该值左右能覆盖到的范围,类似函数区域内的极值。
但是有很多小细节要注意,比如左右只有一边能取到等,这是为了防止重复选取111..这种一大片一样的。

// LUOGU_RID: 102977042
#include <bits/stdc++.h>
#define ll long long

using namespace std;
const int N = 1e6 + 5;
int a[N], n;
ll lMax[N], rMax[N], lMin[N], rMin[N]; //a[i]作为最值的作用范围
stack<int> stk;

int main () {
    cin >> n;
    for (int i = 1; i <= n; i++)    cin >> a[i];
    
    //左Max
    a[0] = 1e9;
    stk.push (0);
    for (int i = 1; i <= n; i++) {
        if (a[i] < a[stk.top ()])    lMax[i] = i - 1, stk.push (i);
        else {
            while (stk.size () && a[i] >= a[stk.top ()])   stk.pop ();
            lMax[i] = stk.top ();
            stk.push (i);
        }
    }

    //左Min
    a[0] = 0;
    while (!stk.empty ())   stk.pop ();
    stk.push (0);
    for (int i = 1; i <= n; i++) {
        if (a[i] > a[stk.top ()])    lMin[i] = i - 1, stk.push (i);
        else {
            while (stk.size () && a[i] <= a[stk.top ()])   stk.pop ();
            lMin[i] = stk.top ();
            stk.push (i);
        }
    }

    //右Max
    a[n + 1] = 1e9;
    while (!stk.empty ())   stk.pop ();
    stk.push (n + 1);
    for (int i = n; i >= 1; i--) {
        if (a[i] < a[stk.top ()])    rMax[i] = i + 1, stk.push (i);
        else {
            while (stk.size () && a[i] > a[stk.top ()])   stk.pop ();
            rMax[i] = stk.top ();
            stk.push (i);
        }
    }

    //右Min
    a[n + 1] = 0;
    while (!stk.empty ())   stk.pop ();
    stk.push (n + 1);
    for (int i = n; i >= 1; i--) {
        if (a[i] > a[stk.top ()])    rMin[i] = i + 1, stk.push (i);
        else {
            while (stk.size () && a[i] < a[stk.top ()])   stk.pop ();
            rMin[i] = stk.top ();
            stk.push (i);
        }
    }

    //for (int i = 1; i <= n; i++)   cout << lMin[i] << ' ' << rMin[i] << ' ' << lMax[i] << ' ' << rMax[i] << endl; 
    
    ll ans = 0;
    for (int i = 1; i <= n; i++) {
        ans += 1ll * a[i] * (1ll * (rMax[i] - i) * (i - lMax[i]) - 1ll * (rMin[i] - i) * (i - lMin[i]));
    }
    cout << ans << endl;
}

//任意子区间的最大最小值之差
//四个单调栈分别维护
//注意细节: 右等左不等

E. Choosing The Commander

01Trie树
按位统计:
当前位为1:统计k=0上子树答案,向k=1子树遍历
当前位为0:遍历k=0子树
有一个不懂的就是为什么下标idx一定要从1开始

// LUOGU_RID: 102992350
#include <bits/stdc++.h>

using namespace std;
const int N = 3100005;
int son[N][2], cnt[N], idx = 1;//下标0的点,是根节点,也是空节点;存节点的子节点;cnt[]存储以每个节点结尾的单词数量

void insert(int x, int d) {
    int p = 1;
    for (int i = 31; i >= 0; i--) {
        bool u = (x >> i) & 1;
        if (!son[p][u])    son[p][u] = ++idx;
        p = son[p][u];
        cnt[p] += d;
    }
}

int query(int x, int y) {
    int p = 1, sum = 0;
    for (int i = 31; i >= 0; i--) {
        bool u = (x >> i) & 1, v = (y >> i) & 1;
        if (v == 1)    sum += cnt[son[p][u]], p = son[p][u^1];
        else    p = son[p][u];
    }
    return sum;
}

int main () {
    int n, op, x, y;
    cin >> n;
    while (n --) {
        cin >> op;
        if (op == 1) {
            cin >> x;
            insert (x, 1);
        }
        else if (op == 2) {
            cin >> x;
            insert (x, -1);
        }
        else {
            cin >> x >> y;
            cout << query (x, y) << endl;
        }
    }
}

//01-trie树
//当前位为1:统计k=0上子树答案,向k=1子树遍历
//当前位为0:遍历k=0子树
//为什么下标从1开始

F. MEX Queries

复杂线段数

posted @ 2023-02-25 20:14  Sakana~  阅读(28)  评论(0编辑  收藏  举报