首页 > 编程语言 > 详细

2021 Spring Discussion 1

时间:2021-03-15 22:54:40      阅读:39      评论:0      收藏:0      [点我收藏+]

2021 Spring Discussion 1

春季学期第一次讨论班,王博士讲了许多 AtCoder 上的思维题。

Slides: https://drive.google.com/file/d/10EVj_812TJNb4OlgBYxoEG-hdldre9X-/view?usp=sharing

Task 1: Pay to win

Source: AtCoder Grand Contest 044, Problem A

Link: https://atcoder.jp/contests/agc044/tasks/agc044_a

Code:

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

typedef long long ll;
ll a, b, c, d, n;
map<ll, ll> f;

ll dfs(ll num) {
    if (f.find(num) != f.end()) {
        return f[num];
    }

    ll res = 1e18;
    if (num < res / d)    res = num * d;
    res = min(res, (num % 2) * d + dfs(num / 2) + a);
    res = min(res, (2 - num % 2) * d + dfs((num + 1) / 2) + a);
    res = min(res, (num % 3) * d + dfs(num / 3) + b);
    res = min(res, (3 - num % 3) * d + dfs((num + 2) / 3) + b);
    res = min(res, (num % 5) * d + dfs(num / 5) + c);
    res = min(res, (5 - num % 5) * d + dfs((num + 4) / 5) + c);
    return f[num] = res;
}

int main() {
    int T;
    cin >> T;
    while(T--) {
        cin >> n >> a >> b >> c >> d;
        f[0] = 0, f[1] = d;
        cout << dfs(n) << endl;
        f.clear();
    }
    return 0;
}

这题还是比较可做的,要注意一下 14 行那个特判。虽然样例足够强,但是太大了,还是很难直接查到这个错的。

Task 2 Jumper

Source: AtCoder Grand Contest 050, Problem A

Link: https://atcoder.jp/contests/agc050/tasks/agc050_a

Code:

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

int main() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        cout << (2 * i) % n + 1 << " " << (2 * i + 1) % n + 1 << endl;
    }
    return 0;
}

这题也挺可做的。由 \(N=1000\)\(10\) 条边数量级也能想到 \(\log\) 关系,不过到二叉树还是稍有困难。

Task 3: Median Sum

Source: AtCoder Grand Contest 020, Problem C

Link: https://atcoder.jp/contests/agc020/tasks/agc020_c

Code:

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

const int N = 4000005;
bitset<N> bst;
int a[N], cnt;

int main() {
    int n, x;
    cin >> n;
    bst[0] = 1;
    for (int i = 0; i != n; ++i) {
        cin >> x;
        bst |= (bst << x);
    }
    for (int i = 1; i != N; ++i) {
        if (bst[i]) {
            a[++cnt] = i;
        }
    }
    cout << a[(cnt + 1) / 2] << endl;
    return 0;
}

其实我不太会用 std::bitset

Task 4: Tree Edges XOR

Source: AtCoder Grand Contest 052, Problem B

Link: https://atcoder.jp/contests/agc052/tasks/agc052_b

Code:

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

template<typename T>
inline void read(T &x) {
    char c = getchar(); x = 0;
    while (c < ‘0‘ || ‘9‘ < c)  c = getchar();
    while (‘0‘ <= c && c <= ‘9‘) {
        x = (x << 1) + (x << 3) + c - 48;
        c = getchar();
    }
}

struct Edge {
    int v, w1, w2;
};
const int N = 131072;
vector<Edge> gra[N];
int n, ori[N], goa[N], fix1, fix2;

void dfs(int u, int pa) {
    ori[1] ^= ori[u], goa[1] ^= goa[u];
    for (auto nex: gra[u]) {
        if (nex.v == pa)    continue;
        ori[nex.v] = ori[u] ^ nex.w1;
        goa[nex.v] = goa[u] ^ nex.w2;
        dfs(nex.v, u);
    }
}

int main() {
    read(n);
    for (int i = 1; i != n; ++i) {
        int u, v, w1, w2;
        read(u), read(v), read(w1), read(w2);
        gra[u].push_back(Edge{v, w1, w2});
        gra[v].push_back(Edge{u, w1, w2});
    }
    dfs(1, 0);
    dfs(1, 0);
    sort(ori + 1, ori + n + 1);
    sort(goa + 1, goa + n + 1);
    for (int i = 1; i <= n; ++i) {
        if (ori[i] != goa[i]) {
            return puts("NO"), 0;
        }
    }
    return puts("YES"), 0;
}

这题实在巧妙。用嘴做的时候就很困难,用手做问题就更多。这里我多说一点 Slides 上没写的实现细节。

Slides 上提到,需要附加条件: \(\bigoplus_{i=1}^n f(i) = 0 ~~~~~~~~ (*)\)

具体地,我设置根节点权为 \(0\) ,做一遍 dfs 得到其他点权。为了保证 \((*)\) 式成立,可以考虑将 \(f(1)\) 赋值为 \(\bigoplus_{i=2}^n f(i)\) ,重做一次 dfs。

这样做似乎非常地丑,也似乎有很好的做法,但是我没想到。

Task 5: Rotation Sort

Source: AtCode Grand Contest 032, Problem D

Link: https://atcoder.jp/contests/agc032/tasks/agc032_d

Code:

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

typedef long long ll;
const int N = 5005;
int n, arr[N];
ll a, b, suf, f[N];

int main() {
    scanf("%d%lld%lld", &n, &a, &b);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &arr[i]);
    }
    
    arr[++n] = 1e9, f[0] = 0;
    for(int i = 1; i <= n; i++) {
        f[i] = 1e16, suf = 0;
        for(int j = i - 1, mos = -1; j >= 0; j--) {
            if(arr[j] < arr[i] && mos < arr[j]) {
                f[i] = min(f[i], f[j] + suf);
            }

            if (arr[i] > arr[j]) {
                mos = max(mos, arr[j]);
                suf += b;
            }
            else    suf += a;
        }
    }

    cout << f[n] << endl;

    return 0;
}

这里顺带提一下动态规划的状态转移方程:

我们记 \(f_i\) 表示固定 \(a_i\) 为所选定上升子序列中的一个元素,改变前 \(i\) 个元素的位置,维护其相对升序所需的最小花费。

那么显然有:

\[\large f_i=\min_{a_j < a_i,~~1 \leq j < i} \{ f_j + \sum_{k = 1}^{i-1} c_i(k) - \sum_{k=1}^jc_i(k) \} \]

其中,

\[c_i(k)=\begin{cases} A & a_k > a_i \B & a_k < a_i \\end{cases} \]

计算 \(c_i(k)\) 时,我们对于每个 \(i\) 维护其后缀和,这样就可以做到 \(O(n^2)\) 的复杂度。

Task 6: Increasing Numbers

Source: AtCoder Grand Conteset 011, Problem E

Link: https://atcoder.jp/contests/agc011/tasks/agc011_e

Code:

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

const int N = 524288;
int len = 0;
short a[N];

int main() {
    char c = getchar();
    while (‘0‘ <= c && c <= ‘9‘) {
        a[len++] = (c - 48) * 9;
        c = getchar();
    }
    reverse(a, a + len);
    for (int i = 0; i <= len; ++i) {
        a[i + 1] += a[i] / 10;
        a[i] %= 10;
    }

    int k = 0, sum = 0;
    for (int i = 0; i <= len; sum += a[i++]);
    while (true) {
        a[0] += 9, sum += 9, ++k;
        for (int i = 0; a[i] >= 10 && i <= len; ++i) {
            sum -= a[i] + a[i + 1];
            a[i] -= 10, ++a[i + 1];
            sum += a[i] + a[i + 1];
        }
        if (sum <= 9 * k)
            return printf("%d\n", k), 0;
    }
    return 0;
}

我的常数实在太大了,再带上 STL 的 Buff 更大得不行, AtCoder 的测评姬跑得上气不接下气(

这题是真的很厉害,一些 basic ideas 组装起来,就变得神乎其神了。

2021 Spring Discussion 1

原文:https://www.cnblogs.com/Shimarin/p/14534359.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!