Educational Codeforces Round 77 (Rated for Div. 2)

A - Heating

题意:一个房间,最多装c个加热器,若某个加热器长度为k,则花费为k*k,覆盖这n个长度求最小花费。

题解:首先每个格子只装最多一个,先取min。然后肯定是最平均最好,小平均值是n/d取下整,大平均值是小平均值+1,大平均值的个数是n%d,小平均值的个数即剩下的。

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

void test_case() {
    ll c, n;
    scanf("%lld%lld", &c, &n);
    c = min(n, c);
    ll L = n / c;
    ll R = L + 1;
    ll cntR = n % c;
    ll cntL = (n - (cntR * R)) / L;
    ll ans = cntL * (L * L) + cntR * (R * R);
    printf("%lld\n", ans);
}

int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    int t = 1;
    scanf("%d", &t);
    for(int ti = 1; ti <= t; ++ti) {
        //printf("Case #%d: ", ti);
        test_case();
    }
}

B - Obtain Two Zeroes

题意:两个数x,y,每次可以选择:1、x:=x-d, y:=y-2d 或 2、x:=x-2d, y:=y-d ,d是正整数,求x,y是否可以同时为0。

题解:首先先把两个拉到同一个大小,这是必须的,两个数第一次相遇的时机。当这个时机出现在负数,NO。出现这个时机之后两个数轮流减,直接模3。

void test_case() {
    int a, b;
    Scanf(a, b);
    if(a > b)
        swap(a, b);
    a -= b - a;
    if(a < 0 || a % 3)
        return NO();
    return YES();
}

C - Infinite Fence

题意:有一串砖,凡是r的倍数而不是b的倍数必须涂红,凡是b的倍数而不是r的倍数必须涂蓝,是公倍数则选一个涂。把涂色的砖选出来之后,问是否一定有连续的k个砖是同一种颜色。

题解:首先,假如两个数有公因子,那么他们都会跳过一系列砖,除掉不影响答案。然后必有他们的gcd为1,若b和a相等,则NO。否则设b更大,那么必须贪心,有b涂就涂b打断r的连续。那么因为公因子是1,那么在某个时刻会存在这样的情形:

br---r---r---rb
br---r---r---r.b
br---r---r---r..b
br---r---r---r...b

也就是一个b之后若干个r周期,然后剩下至多r-1个空格不够再放一个r。已知放k个r需要d=(k-1)*r+1的长度,求b-1的长度是否超过d,注意溢出。然后若b和a相等,也满足此式,合并一起。

void YES() {
    //puts("YES");
    puts("REBEL");
}

void NO() {
    //puts("NO");
    puts("OBEY");
}

void test_case() {
    ll r, b, k;
    Scanf(r, b, k);
    if(r > b)
        swap(r, b);
    ll g = __gcd(r, b);
    r /= g, b /= g;
    if((b - 1) >= (k - 1) * r + 1)
        return YES();
    else
        return NO();
}

D - A Game with Traps

题意:有m个士兵,t秒,你要带尽可能多的士兵从0去n+1,且他们不能被杀死。路上有一些陷阱,陷阱d[i]会杀死能力比它小的士兵,陷阱位置在l[i],当你走到r[i]时可以拆除它。每次你可以向左或者向右移动,然后立刻拆除当前位置的陷阱。你的士兵必须被你带着移动,且移动到格子时该位置不能有陷阱。

题解:首先可以二分士兵的数量,然后贪心选最强的士兵出来。check的时候也是直接贪心,发现一个规律,就是假如遇到了一个陷阱,则必须要拆除它,经过2次R-L再把士兵带过来。而如果拆的路上还有其他陷阱的话就一路拆过去,因为这些路拆除陷阱至少都要走两次,走过去一起拆了不会更差,拆完一路之后干脆就一起带过来所以走了3倍路程。否则若没有陷阱肯定贪心直接带着士兵右移1格。

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

int m, n, k, t;
int a[200005];
int R[200005];
vector<int> l[200005];
int d[200005];

bool check(int p) {
    int predp = 0, curdp = 0;
    for(int i = 1, prei = 0; i <= n + 1; ++i) {
        int maxR = -1;
        for(auto id : l[i]) {
            if(d[id] <= a[p])
                continue;
            maxR = max(maxR, R[id]);
        }
        while(i < maxR) {
            ++i;
            for(auto id : l[i]) {
                if(d[id] <= a[p])
                    continue;
                maxR = max(maxR, R[id]);
            }
        }
        if(maxR != -1)
            curdp = predp + 3 * (i - prei);
        else
            curdp = predp + 1;
        prei = i;
        predp = curdp;
    }
    return curdp <= t;
}

void test_case() {
    scanf("%d%d%d%d", &m, &n, &k, &t);
    for(int i = 1; i <= m; ++i)
        scanf("%d", &a[i]);
    for(int i = 1, L; i <= k; ++i) {
        scanf("%d%d%d", &L, &R[i], &d[i]);
        l[L].push_back(i);
    }
    sort(a + 1, a + 1 + m, greater<int>());
    int l = 0, r = m;
    while(1) {
        int mid = (l + r) >> 1;
        if(l == mid) {
            if(check(r)) {
                printf("%d\n", r);
                return;
            }
            printf("%d\n", l);
            return;
        }
        if(check(mid))
            l = mid;
        else
            r = mid - 1;
    }
}

int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    int t = 1;
    //scanf("%d", &t);
    for(int ti = 1; ti <= t; ++ti) {
        //printf("Case #%d: ", ti);
        test_case();
    }
}

/*
    1. 小数据问题退化:
        输入为0或1会不会有特殊情况?其他的比如最小环要有三个点,强连通分量缩到最后一个点等等。
    2. 内存:
        内存的空间有没有开够?有时有反向边,有时有额外新加的边。线段树开了4倍了吗?
        可持久化数据结构会不会内存溢出?多组数据时vector会不会翻车?
        多组数据有进行初始化吗?memset会不会翻车?
    3. 算术溢出:
        乘法上溢出了?忘记取模了?输入输出用了%d?让无符号数越到负数了?
    4. 习惯:
        函数都有指定返回值吗?返回值非void函数的每一个分支都有显式的返回值吗?确定不会进入的分支可以assert一把。
        Yes和No有没有反?有没有搞错n和m?离散化之后的cn有没有错?换行和空格要怎么整?priority要把符号反过来。
    5. 其他bug:
        基环树是树加环,不是链加环。
*/

E - Tournament

题意:有n个选手,n是2的幂。n个人的能力值分别是[1,n],两两配对参加淘汰赛,每次高手会淘汰掉低手,然后高手们参加下一轮。你的朋友也参加,安排适当的顺序让他获胜,你可以贿赂其中的某些人,使得他和你朋友打的时候放水。求最便宜的贿赂总价。

题解:若朋友是天下第一,则0。否则必定会和天下第一打,所以要贿赂天下第一。然后天下第一可以带走[n/2+1,n]这群高手,若朋友在[n/2+1,n]里则直接结束了,否则递归到一半规模的问题。不过这一次不一定要贿赂天下第一(感谢样例2),你可以贿赂一个比天下第一更强的又更便宜的人和天下第一交换。所以维护一个小根堆是最简单的方法,当然也可以动态保留至多logn个人,更贵的就没用了,或者整个归并排序,这样复杂度可能会变小,但是麻烦还容易错。要注意贿赂的人不用重复入队。

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

int a[(1 << 18) + 5];
priority_queue<int, vector<int>, greater<int> >pq;

ll solve(int pos, int n) {
    if(pos >= n)
        return 0;
    else {
        ll res = 0;
        if(pq.empty() || a[n] < pq.top()) {
            res += a[n];
            for(int i = n / 2 + 1; i < n; ++i)
                pq.push(a[i]);
        } else {
            res += pq.top();
            pq.pop();
            for(int i = n / 2 + 1; i <= n; ++i)
                pq.push(a[i]);
        }
        res += solve(pos, n / 2);
        return res;
    }
}

void test_case() {
    int n;
    scanf("%d", &n);
    int p = -1;
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
        if(a[i] == -1)
            p = i;
    }
    printf("%lld\n", solve(p, n));
}

int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    int t = 1;
    //scanf("%d", &t);
    for(int ti = 1; ti <= t; ++ti) {
        //printf("Case #%d: ", ti);
        test_case();
    }
}
posted @ 2019-11-28 13:06  KisekiPurin2019  阅读(335)  评论(0编辑  收藏  举报