2024牛客寒假算法基础集训营2

A-Tokitsukaze and Bracelet

分类讨论

#include<bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
using ldb = long double;

#define int i64

using vi = vector<int>;

using pii = pair<int, int>;
using vii = vector<pii>;

const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 998244353;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n;
    cin >> n;
    for (int a, b, c, res; n; n--) {
        cin >> a >> b >> c, res = 0;
        res += (a - 100) / 50;
        if (34 <= b and b <= 40) res += 1;
        else if (b == 45) res += 2;
        if (34 <= c and c <= 40) res += 1;
        else if (c == 45) res += 2;
        cout << res << "\n";
    }
    return 0;
}

B-Tokitsukaze and Cats

其实就是特判每只周围有没有猫即可

#include<bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
using ldb = long double;

#define int i64

using vi = vector<int>;

using pii = pair<int, int>;
using vii = vector<pii>;

const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 998244353;

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, m, k;
    cin >> n >> m >> k;
    vector e(n + 1, vi(m + 1));
    for (int i = 1, x, y; i <= k; i++)
        cin >> x >> y, e[x][y] = 1;
    int res = k * 4;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) {
            if (e[i][j] != 1) continue;
            if (i + 1 <= n and e[i + 1][j]) res--;
            if (j + 1 <= m and e[i][j + 1]) res--;
        }
    cout << res << "\n";
    return 0;
}

C-Tokitsukaze and Min-Max XOR

因为是求子序列,所以可以对\(a\)进行排序。

排序后如果选择了\([l,r]\),则\(min=a_l,max=a_r\),而中间的数可以随便选,所以贡献就是\(2^{r-l-1}\)

这样枚举端点的话复杂度是\(O(n^2)\),我们先考虑\(2^{r-l-1}=\frac{2^{r-1}}{2^{l}}\),因此对于一个复合条件的\(r\)所有合法的\(l\)贡献和为\(2^{r-1}\cdot\sum \frac{1}{2^l}\),所以我们可以枚举\(r\)然后快速的求和

我们可以考虑用 01Tire来维护这个信息,首先就是把所有的二进制串对其,然后后对于每插入一个串,其在路径上的贡献就是\(\frac{1}{2^i}\)

首先\(x\oplus y\le k\),要满足这样的条件\(x\oplus y\)一定存在一个前缀等于\(k\),然后下一位\(k\)\(1\)\(x\oplus y\)\(0\),再之后的位就可以随便取了。

因此我们从根开始查找,如果这一位\(k\)\(1\),那么当前位可以异或和\(0\)则当前节点子树中的所有串均符合条件,然后向着让当前位异或后和\(k\)相同的方向走即可。

#include <bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;

#define int i64

using vi = vector<int>;


const int mod = 1e9 + 7;

struct Node {
    int sum = 0;
    array<Node *, 2> ch;

    Node() {
        ch.fill(nullptr);
    }
};

const int N = 2e5;
vi pw(N), inv(N);


void solve() {
    int n, k;
    cin >> n >> k;
    vector<int> a(n);
    for (auto &i: a) cin >> i;
    sort(a.begin(), a.end());

    Node *root = new Node();
    int res = 0;
    for (int i = 0; i < n; i++) {
        auto p = root;
        int ans = 0;
        for (int j = 29, w, v; j >= 0 and p != nullptr; j--) {
            w = (a[i] >> j) & 1, v = (k >> j) & 1;
            if (v == 1) {
                if (p->ch[w] != nullptr) ans += p->ch[w]->sum;
                p = p->ch[w ^ 1];
            } else {
                p = p->ch[w];
            }
        }
        if (p != nullptr) ans += p->sum;
        res = (res + 1 + ans % mod * pw[i - 1] % mod) % mod;
        p = root;
        for (int j = 29, w; j >= 0; j--) {
            w = (a[i] >> j) & 1;
            if (p->ch[w] == nullptr) p->ch[w] = new Node();
            p = p->ch[w];
            p->sum = (p->sum + inv[i]) % mod;
        }
    }
    cout << res % mod << "\n";
    return;
}

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    pw[0] = inv[0] = 1;
    for (int i = 1; i < N; i++)
        pw[i] = pw[i - 1] * 2 % mod, inv[i] = inv[i - 1] * (mod + 1) / 2 % mod;
    int TC;
    for (cin >> TC; TC; TC--)
        solve();
    return 0;
}

D-Tokitsukaze and Slash Draw

每一个操作可以无限次的使用,这样的话就可以当成一个完全背包来做。\(f[i]\)表示把一击必杀移动\(i\)的最小花费

#include<bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
using ldb = long double;

#define int i64

using vi = vector<int>;

using pii = pair<int, int>;
using vii = vector<pii>;

const int inf = INT_MAX, INF = 1e18;
const int mod = 998244353;

void solve() {
    int n, m, k;
    cin >> n >> m >> k;
    vi f(n, INF);
    f[0] = 0;
    for (int i = 0, a, b, flag; i < m; i++) {
        cin >> a >> b;
        do {
            flag = 0;
            for (int j = 0, t; j < n; j++) {
                if (f[j] == INF) continue;
                t = (j + a) % n;
                if (f[j] + b < f[t])
                    f[t] = f[j] + b, flag = 1;
            }
        } while (flag);
    }
    int res = f[n-k];
    if( res == INF ) res = -1;
    cout << res << "\n";
}

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int TC;
    for (cin >> TC; TC; TC--)
        solve();
    return 0;
}

E-Tokitsukaze and Eliminate (easy)

其实只要动态维护每个颜色最靠右的位置,以及当前所有最靠右的最小值,然后贪心即可

#include<bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
using ldb = long double;

#define int i64

using vi = vector<int>;

using pii = pair<int, int>;
using vii = vector<pii>;

const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 998244353;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};

void solve() {
    int n;
    cin >> n;
    vector<vi> cnt(2);
    for (int i = 1, x; i <= n; i++)
        cin >> x, x--, cnt[x].push_back(i);
    int res = 0;
    while (not cnt[0].empty() and not cnt[1].empty()) {
        res++;
        int i = 0;
        if (cnt[1].back() < cnt[0].back()) i = 1;
        while (not cnt[i ^ 1].empty() and cnt[i ^ 1].back() > cnt[i].back()) cnt[i ^ 1].pop_back();
        cnt[i].pop_back();
    }

    res += cnt[0].size() + cnt[1].size();
    cout << res << "\n";
}

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int TC;
    for (cin >> TC; TC; TC--)
        solve();

    return 0;
}

F-Tokitsukaze and Eliminate (hard)

这里颜色很多。我们看删除什么样的后缀最优呢?就是恰好包含所有种类的颜色是最优的,因为在往前走一定是重复的,因此必定无法通过一次操作删除。知道这个结论后只要倒序遍历一遍即可。

#include<bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
using ldb = long double;

#define int i64

using vi = vector<int>;

using pii = pair<int, int>;
using vii = vector<pii>;

const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 998244353;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;

void solve() {
    int n;
    cin >> n;
    vi a(n), b(n);
    int cnt = 0, t = 0;
    for (auto &i: a)
        cin >> i, i--, b[i]++, cnt += b[i] == 1;
    reverse(a.begin(), a.end());
    set<int> s;
    int res = 0;
    for (const auto &i: a) {
        b[i]--;
        if (b[i] == 0) t++;
        s.insert(i);
        if (s.size() == cnt)
            res++, s.clear(), cnt -= t, t = 0;
    }
    cout << res << "\n";

}

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int TC;
    for (cin >> TC; TC; TC--)
        solve();

    return 0;
}

I-Tokitsukaze and Short Path (plus)

容易推得绕路一定不优,所以就是所有边之和就是答案,因此第一个绝对值就能去掉,只剩下第二个绝对值。我们排序,然后维护前缀和后缀和即可。

#include<bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
using ldb = long double;

#define int i64

using vi = vector<int>;

using pii = pair<int, int>;
using vii = vector<pii>;

const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 998244353;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;

void solve() {
    int n;
    cin >> n;
    vi a(n);
    int res = 0, sum = 0;
    for (auto &i: a) cin >> i, sum += i;
    res += sum * (n - 1) *2;
    sort(a.begin(), a.end());
    for (int i = 0, x = 0, y = n, pre = 0, suf = sum; i < n; i++) {
        res += (x * a[i] - pre) + (suf - y * a[i]);
        x++, y--, pre += a[i], suf -= a[i];
    }
    cout << res << "\n";
}

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int TC;
    for (cin >> TC; TC; TC--)
        solve();

    return 0;
}

J-Tokitsukaze and Short Path (minus)

通过简单的分类讨论就可以推得\(w_{u,v}=2\min(a_u,a_v)\)。因此最短路有两种情况,一种是直接走,另一种是绕路的最小值。

因此我们可以把所有的点排序,然后考虑从当前点出发是直接走短还是绕路短,并且计算出来回的方案数即可

#include<bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
using ldb = long double;

#define int i64

using vi = vector<int>;

using pii = pair<int, int>;
using vii = vector<pii>;

const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 998244353;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};

void solve() {
    int n;
    cin >> n;
    vi a(n);
    int res = 0;
    for (auto &i: a) cin >> i;
    sort(a.begin(), a.end());
    int d = a.front(), t = n;
    for (auto &i: a) {
        t--;
        res += min(4 * d, i * 2) * t * 2;
    }
    cout << res << "\n";
}

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int TC;
    for (cin >> TC; TC; TC--)
        solve();

    return 0;
}

K-Tokitsukaze and Password (easy)

判断一个数是不是8的倍数,只需要判断最低三位即可。

所以可以先爆搜出8的倍数,然后再爆搜其他位置即可

#include<bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
using ldb = long double;

#define int i64

using vi = vector<int>;

using pii = pair<int, int>;
using vii = vector<pii>;

const int inf = INT_MAX, INF = 1e18;
const int mod = 998244353;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};

int n, y, res;

set<int> vis;

void dfs(string s, i32 i) {
    if (i == 3) {
        int x = 0;
        for (int j = 0, t = 1; j < 3; j++, t *= 10)
            x += (s[j] - '0') * t;
        if (x % 8 != 0) return;
    }
    if (i == n) {
        if (s.back() == '0' and n > 1) return;
        int x = 0;
        for (int j = 0, t = 1; j < n; j++, t *= 10)
            x += (s[j] - '0') * t;
        if (x % 8 != 0 or x > y) return;
        res++;
        return;
    }

    if (s[i] >= '0' and s[i] <= '9') {
        dfs(s, i + 1);
        return;
    } else if (s[i] == '_') {
        for (s[i] = '0'; s[i] <= '9'; s[i]++)
            dfs(s, i + 1);
        s[i] = '_';
        return;
    } else {
        for (int j = '0'; j <= '9'; j++) {
            if (vis.count(j)) continue;
            vis.insert(j);
            auto t = s;
            for (auto &l: t)
                if (l == s[i]) l = j;
            dfs(t, i + 1);
            vis.erase(j);
        }
    }
}


void solve() {
    string s;
    cin >> n >> s >> y, res = 0;
    reverse(s.begin(), s.end()), vis.clear();
    dfs(s, 0);
    cout << res << "\n";
}

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int TC;
    for (cin >> TC; TC; TC--)
        solve();
    return 0;
}
posted @ 2024-03-06 17:32  PHarr  阅读(17)  评论(0编辑  收藏  举报