AtCoder Beginner Contest 303

发布时间 2023-06-01 11:06:21作者: PHarr

A - Similar String

#include <bits/stdc++.h>

using namespace std;

#define int long long

int32_t main() {
    int n;
    string s , t;
    cin >> n >> s >> t;
    for( int i = 0 ; i < n ; i ++ ){
        if( s[i] == t[i] ) continue;
        if( s[i] == '1' && t[i] == 'l' ) continue;
        if( s[i] == 'l' && t[i] == '1' ) continue;
        if( s[i] == '0' && t[i] == 'o' ) continue;
        if( s[i] == 'o' && t[i] == '0' ) continue;
        cout << "No\n";
        return 0;
    }
    cout << "Yes\n";
    return 0;
}

B - Discord

#include <bits/stdc++.h>

using namespace std;

#define int long long

int32_t main() {
    int n , m;
    cin >> n >> m;
    vector<vector<int>> a( n+1 , vector<int>(n+1 , 0 ));
    for( ; m ; m -- ){
        for( int u = -1 , v , i = 1 ; i <= n ; i ++ ){
            cin >> v;
            if( u == -1 ) u = v;
            else a[u][v] = a[v][u] = 1 , u = v;
        }
    }
    int res = 0;
    for( int i = 1 ; i <= n ; i ++ )
        for( int j = i + 1 ; j <=n ;j ++ ){
            if( a[i][j] == 1 ) continue;
            res ++;
        }
    cout << res;
    return 0;
}

C - Dash

模拟就好了,注意每个点的补给只能用一次

#include <bits/stdc++.h>

using namespace std;

#define int long long

int32_t main() {
    int n , m , h , k;
    cin >> n >> m >> h >> k;
    string s;
    cin >> s;
    set<pair<int,int>> rec;
    for( int x , y ; m ; m -- )
        cin >> x >> y , rec.emplace( x , y );
    int r = 0 , c = 0;
    for( auto i : s ){
        if( h == 0 ){
            cout << "No\n";
            return 0;
        }
        if( i == 'R' ) r ++;
        else if( i == 'L' ) r --;
        else if( i == 'U' ) c ++;
        else c --;
        h --;
        if( h < k && rec.count(make_pair(r,c)) ) h = k , rec.erase(make_pair(r,c));
    }
    cout << "Yes\n";
    return 0;
}

D - Shift vs. CapsLock

\(f[i][0]\)表示前\(i\)位,且当前CapsLock没按下时的最小花费,\(f[i][1]\)表示前\(i\)位,且当前CapsLock按下时的最小花费。

然后模拟转移的过程即可。

#include <bits/stdc++.h>

using namespace std;

#define int long long

int32_t main() {
    int x, y, z, n;
    string s;
    cin >> x >> y >> z >> s;
    n = s.size();

    vector<array<int, 2>> f(n + 1, array{(int) 1e18, (int) 1e18});
    f[0][0] = 0;
    for (int i = 1; i <= n; i++) {
        if (s[i - 1] == 'a') {
            f[i][0] = min({f[i-1][0] + x, f[i - 1][1] + y + z, f[i - 1][1] + z + x});
            f[i][1] = min({f[i-1][1] + y, f[i - 1][0] + x + z, f[i - 1][0] + z + y});
        } else {
            f[i][0] = min({f[i - 1][0] + y, f[i - 1][1] + x + z, f[i - 1][1] + z + y});
            f[i][1] = min({f[i - 1][1] + x, f[i - 1][0] + y + z, f[i - 1][0] + z + x});
        }
    }
    cout << min( f[n][0] , f[n][1] );

    return 0;
}

E - A Gift From the Stars

我们把星星的中间的点叫做 root 点,我们的任务就是找到所有的 root 点,并且要输出 root 点的度数。

首先我们把最终的树拿出来,所有的叶子点走一步就一定是 root 点 ,而 root 点链接的所有的点都是星星的叶子点,如果星星的叶子点的度数不为 1,就说明了这个星星的叶子点和其他的星星的叶子点相连。

这样的话我们通过一个类似求拓扑序的方式就可以找到所有的 root 点了。

#include <bits/stdc++.h>

using namespace std;

#define int long long

int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int n;
    cin >> n;
    vector<vector<int>> e(n + 1);
    vector<int> d(n + 1);
    for (int i = n - 1, u, v; i; i--)
        cin >> u >> v, e[u].push_back(v), e[v].push_back(u), d[u]++, d[v]++;
    queue<int> q;
    vector<bool> vis(n + 1, 0);
    vector<int> res;
    for( int i = 1 ; i <= n ; i ++ )
        if( d[i] == 1 ) q.push(i);
    while( !q.empty() ){
        int u = q.front(); q.pop();
        if( vis[u] ) continue;
        vis[u] = 1;
        int root;
        for( auto i : e[u] ){
            if( vis[i] ) continue;
            root = i;
            break;
        }
        res.push_back( e[root].size() );

        vis[root] = 1;
        for( auto i : e[root] ){
            d[i] --;
            vis[i] = 1;
            if( d[i] == 1 ) {
                for( auto j : e[i] ){
                    if(vis[j]) continue;
                    q.push(j);
                    break;
                }
            }
        }
    }
    sort( res.begin() , res.end() );
    for( auto i : res )
        cout << i << " ";
    return 0;
}

9C4558897E5E72F34B4C36D04E8D2CBA

然后我们发现所有的图其实都是这种样子的。所以从选择任何一个叶子点作为跟求一遍深度,所有深度为\(2,5,8,11,14,17\dots\)的点都一定是 root 点。

#include <bits/stdc++.h>

using namespace std;

#define int long long

vector<vector<int>> e;
vector<int> dep;

void dfs(int x) {
    for (auto i: e[x]) {
        if (dep[i]) continue;
        dep[i] = dep[x] + 1, dfs(i);
    }
    return;
}

int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int n;
    cin >> n;
    e = vector<vector<int>>(n + 1);
    dep = vector<int>(n + 1, 0);
    for (int i = n - 1, u, v; i; i--)
        cin >> u >> v, e[u].push_back(v), e[v].push_back(u);
    for (int i = 1; i <= n; i++) {
        if (e[i].size() != 1) continue;
        dep[i] = 1, dfs(i);

    }
    vector<int> res;
    for( int i = 1 ; i <= n ; i ++ ){
        if( (dep[i] - 2) % 3  ) continue;
        res.push_back( e[i].size() );
    }
    sort(res.begin(), res.end());
    for (auto i: res)
        cout << i << " ";
    return 0;
}

F - Damage over Time

首先这道题的难点在于攻击的持续时间无法确定。所以我们考虑二分的方式来枚举攻击的时间。

我们将技能按照\(t_i\)升序排列。如果剩余时间\(t\ge t_i\)那么技能\(i\)造成的伤害就是\(t_i\times d_i\),反之伤害就是\(t\times d_i\)

这样的话我们维护前缀\(t_i\times d_i\)的最大值和后缀\(d_i\)的最大值。这样的话我们就可以选择两种中伤害较大的部分。

如果我们选择\(t\ge t_i\)的情况,那么我们就持续使用\(cnt=t-t_i+1\)次技能。这样造成的伤害最大\(cnt\times t_i\times d_i\),之后就只能使用第二种技能。

如果我们使用第二种技能,我们持续使用$cnt= t-\left \lfloor \frac{max1}{d_i} \right \rfloor \(,其中\)max1\(是第一种能够造成伤害的最大值。这样造成伤害\)d_i\frac{cnt(t+t+1-cnt)}{x}$

#include<bits/stdc++.h>

using namespace std;

#define int __int128
#define ll long long
#define mp make_pair

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(), h = read();
    vector<pair<int, int>> a(n);
    for (auto &[t, d]: a)
        t = read(), d = read();
    sort(a.begin(), a.end());
    vector<pair<int, int> > pre(n);
    pre[0] = mp(a[0].first * a[0].second, a[0].first);
    for (int i = 1, t; i < a.size(); i++) {
        pre[i] = pre[i - 1], t = a[i].first * a[i].second;
        if (t > pre[i].first) pre[i] = mp(t, a[i].first);
    }


    vector<int> suf(n + 1, 0);
    for (int i = n - 1; i >= 0; i--)
        suf[i] = max(suf[i + 1], a[i].second);

    int l = 0, r = h, mid, res;

    auto check = [n, h, a, pre, suf](int x) {
        int pos = n - 1, cur = h;
        while (cur > 0 && x > 0) {
            while (pos >= 0 && a[pos].first > x) pos--;
            if (pos < 0 || pre[pos].first < suf[pos + 1] * x) {
                int down = 0;
                if (pos >= 0) down = pre[pos].first / suf[pos + 1];
                int cnt = x - down;
                int sum = cnt * (x - cnt + 1 + x) / 2 * suf[pos + 1];
                cur -= sum, x -= cnt;
            } else {
                int cnt = x - pre[pos].second + 1;
                cur -= cnt * pre[pos].first, x -= cnt;
            }
        }
        return cur <= 0;
    };

    while (l <= r) {
        mid = (l + r) >> 1;
        if (check(mid)) res = mid, r = mid - 1;
        else l = mid + 1;
    }
    cout << (ll) res;
    return 0;
}