whp 4 - Educational Codeforces Round 121 (Rated for Div. 2)

Educational Codeforces Round 121 (Rated for Div. 2)

Educational Codeforces Round 121 (Rated for Div. 2)

edu经典手速场
竟然有断电这种事,难得觉得能上分

A. Equidistant Letters

题意:给一个串,每个字母最多出现两次,要求重排这个串将出现两次的字母的间隔搞成一样

出现一次的字母直接输出,出现两次的分两次输出

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N = 30;
int cnt[N];

signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

    int T;
    cin >> T;
    while (T--) {
        memset(cnt, 0, sizeof cnt);
        string s;
        cin >> s;
        int sz = s.size();
        for (int i = 0; i < sz; ++i) {
            cnt[s[i]-'a']++;
        }
        for (int i = 0; i < 26; ++i) {
            if (cnt[i] == 1) cout << (char) ('a' + i);
        }
        for (int i = 0; i < 26; ++i) {
            if (cnt[i] == 2) cout << (char) ('a' + i);
        }
        for (int i = 0; i < 26; ++i) {
            if (cnt[i] == 2) cout << (char) ('a' + i);
        }
        cout << endl;
    }

    return 0;
}

B. Minor Reduction

题意:给一个大数,可以执行且必须一次操作,操作定义为选取两个数替换成他们数位和

两个数相加最大不会到20,第一位最多是1,如果两数超过10,大数位数不变,比位数-1的肯定大,这时候尽量在末尾替换

如果位数一定要减说明任意两数相加都没有超过9,此时让第一位数变大是最好的

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;


signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

    int T;
    cin >> T;
    while (T--) {
        string s;
        cin >> s;
        int sz = s.size();
        bool flag = 0;
        int pos = -1;
        for (int i = 0; i < sz - 1; ++i) {
            int x = s[i] - '0';
            int y = s[i + 1] - '0';
            if (x + y > 9) {
                flag = 1;
                pos = i;
            }
        }
        if (flag) {
            int res = (s[pos] - '0') + (s[pos + 1] - '0');
            s[pos] = (char) ((res / 10) + '0');
            s[pos + 1] = (char) ((res % 10) + '0');
            cout << s << endl;
            continue;
        }
        int res = (s[0] - '0') + (s[1] - '0');
        s[1] = (char) (res + '0');
        for (int i = 1; i < sz; ++i) cout << s[i];
        cout << endl;
    }

    return 0;
}

C. Monsters And Spells

题意:有一些怪,每个有一个血量h,并且会在k时刻到来,可以释放魔法,如果在前一时刻释放过魔法造成了x伤害,当前时刻释放魔法可以造成x+1或1的伤害,如果前一时刻没有释放魔法则当前时刻释放魔法只能造成1的伤害。造成num点伤害的魔法需要num法力,知道每个怪什么时候来以及他们的血量,杀每个怪需要在怪到达的时候恰好释放等于或高于其血量的魔法问至少需要耗费多少法力

就是说在k时刻要达成h的魔法,至少要在k-h+1时刻就开始放第一个魔法然后连续+1地放魔法到k时刻,中间断了就不能达到。

就会有很多个需要连续放魔法的区间,将区间合并按每段的长度计算贡献

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const ll N = 1e4 + 10;
ll k[N], h[N];

struct node {
    ll l, r;
};

vector<node> vec, res;

signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

    ll T;
    cin >> T;
    while (T--) {
        vec.clear(), res.clear();
        ll n;
        cin >> n;
        for (ll i = 1; i <= n; ++i) cin >> k[i];
        for (ll i = 1; i <= n; ++i) cin >> h[i];

        for (ll i = 1; i <= n; ++i) {
            vec.push_back({k[i] - h[i] + 1, k[i]});
        }
        sort(vec.begin(), vec.end(), [&](node x, node y) {
            if (x.l != y.l)return x.l < y.l;
            return x.r < y.r;
        });

        ll siz = vec.size();
        ll l = vec[0].l;
        ll r = vec[0].r;
        for (ll i = 1; i < siz; ++i) {
            if (vec[i].l <= r) {
                r = max(r, vec[i].r);
            } else {
                res.push_back({l, r});
                l = vec[i].l;
                r = vec[i].r;
            }
        }
        res.push_back({l, r});

        ll ans = 0;
        siz = res.size();
        for (ll i = 0; i < siz; ++i) {
            ll len = res[i].r - res[i].l + 1;
            ans += (1 + len) * len / 2;
        }
        cout << ans << endl;
    }

    return 0;
}

D. Martial Arts Tournament

题意:有n个人每人重量 a i a_i ai , 要分成三组,第一组重量 < x < x <x,最后一组重量 ≥ y \ge y y,其余为中间这组,可以给每组额外加人,每组最后人数不能为0且需要是2的幂次,问x,y任取的情况下最少加多少人。

枚举最后一组的位置(y),对于每个位置枚举第一组最后的人数(2的幂次那个人数)和第二组的最后人数,计算每组要加多少人

用了upper_bound什么的搞得略复杂。

也可以第一组和最后一组枚举最后人数,双指针这样的时间复杂度应该小一点

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const ll N = 2e5 + 10;
const ll inf = 0x3f3f3f3f3f3f3f3f;

ll a[N], b[N], pre[N];

struct node {
    ll num, cnt;
};

vector<node> vec;

inline void init(ll n) {
    for (ll i = 0; i <= n; ++i) b[i] = 0;
    vec.clear();
    vec.push_back({-1, -1});
}

inline ll calc(ll x) {
    ll pp = 1;
    while (pp < x) {
        pp = pp << 1;
    }
    return pp - x;
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

    ll T;
    cin >> T;
    while (T--) {
        ll n;
        cin >> n;
        init(n);
        for (ll i = 1; i <= n; ++i) {
            cin >> a[i];
            b[a[i]]++;
        }
        for (ll i = 1; i <= n; ++i) {
            if (b[i] == 0)continue;
            vec.push_back({i, b[i]});
        }
        ll siz = vec.size();
        for (ll i = 1; i < siz; ++i) {
            pre[i] = pre[i - 1] + vec[i].cnt;
        }
        // < x //  //>= y//  num > 0 !
        ll ans = inf;
        for (ll i = siz; i > 0; --i) {
            ll tmpc = calc(n - pre[i - 1]); // >= num[i]   =>   part c
            ll tmpa = -1, tmpb = -1;
            for (ll mm = 0; (1ll << mm) <= pre[i - 1]; ++mm) {
                ll numa = (1 << mm);
                ll posx = (ll) (upper_bound(pre + 1, pre + i - 1, numa) - (pre )); // < num[posx]  => part a
                tmpa = calc(pre[posx - 1]);
                tmpb = calc(pre[i - 1] - pre[posx - 1]);
                ll tmp = tmpa + tmpb + tmpc;
                if (tmp < ans) ans = tmp;
            }
            for (ll mm = 0; (1ll << mm) <= pre[i - 1]; ++mm) {
                ll numb = (1 << mm);
                ll posx = (ll) (upper_bound(pre + 1, pre + i - 1, pre[i - 1] - numb) - (pre )); // < num[posx]  => part a
                tmpa = calc(pre[posx - 1]);
                tmpb = calc(pre[i - 1] - pre[posx - 1]);
                ll tmp = tmpa + tmpb + tmpc;
                if (tmp < ans) ans = tmp;
            }
        }
        cout << ans << endl;
    }
}

E. Black and White Tree

题意:有棵树,每个点是黑点或白点,会给出,定义操作是以每个点为起点选择一个黑点向黑点方向前进一步,相邻两次操作不能选一样的点,问每个点能否在若干次操作后到达黑点
(已经是黑点的就是0步操作到黑点)

hoshimi同学写的很好,偷了:
四舍五入他天天帮我写博客(啊对对对)
1.就是首先无法到达(输出0)的点有个必要条件,就是白点所在的白色联通块肯定和所有黑点相邻,当然与黑点直接相邻的白点肯定是能直接到达的。
2.但是有种特殊情况就是存在一个点和黑点相邻同时另一个方向的子树里也有黑点,会使其他方向子树都能到达。(但这种情况只在黑点数为2时会有点无法到达,2以上的这种情况都是全都能到达(全输出1))
3.然后发现1+2等价于,先存下一开始黑点数量k,然后把黑点周围的点也变黑,然后某个白联通块相邻的黑点数为k时整个白联通块都是无法到达。
在这里插入图片描述

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N = 3e5 + 10;

int c[N];
vector<int> g[N], black;
bool vis[N];
int k;

inline void bfs(int x) {
    vector<int> tmpblack, tmpwhite;
    tmpwhite.push_back(x);

    queue<int> que;
    que.push(x);
    vis[x] = 1;

    while (!que.empty()) {
        int tmp = que.front();
        que.pop();
        for (auto to :g[tmp]) {
            if (c[to]) tmpblack.push_back(to);
            else {
                if (vis[to]) continue;
                vis[to] = 1;
                tmpwhite.push_back(to);
                que.push(to);
            }
        }
    }

    sort(tmpblack.begin(), tmpblack.end(), [&](int x, int y) { return x < y; });
    tmpblack.erase(unique(tmpblack.begin(), tmpblack.end()), tmpblack.end());
    if (tmpblack.size() == k) return;

    for (auto u : tmpwhite) c[u] = 1;
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

    int n;
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        cin >> c[i];
        if (c[i] == 1) black.push_back(i);
    }

    for (int i = 1; i < n; ++i) {
        int x, y;
        cin >> x >> y;
        g[x].push_back(y);
        g[y].push_back(x);
    }

    int siz = black.size();
    k = siz;
    for (int i = 0; i < siz; ++i) {
        for (auto to : g[black[i]]) {
            c[to] = 1;
        }
    }

    for (int i = 1; i <= n; ++i) {
        if (vis[i] || c[i]) continue;
        vis[i] = 1;
        bfs(i);
    }
    for (int i = 1; i <= n; ++i) cout << c[i] << " ";
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值