zoukankan      html  css  js  c++  java
  • Codeforces Round #377 (Div. 2)

    题目链接:https://codeforces.com/contest/732

    A - Buy a Shovel

    题意:有无穷枚10元硬币和1枚r元硬币,且r是[1,9]。每个物品的单价是x元,求最少买多少件不用找钱。

    题解:记得区分用r元和不用r元两种情况,分别对应模10的余数为r或者为0。现在枚举不超过10个就可以出结果。

    B - Cormen --- The Best Friend Of a Man

    题意:宠物狗每连续两天都需要总共走至少k步,注意对于[0,1]和[n,n+1]没有这个要求。给出一个数列表示第几天走几步,增加最少的步使得[1,2],[2,3]...[n-1,n]都满足每连续两天都需要总共走至少k步。

    题解:贪心往右边的一天放。注意最后一天。

    C - Sanatorium

    题意:某人在食堂待了连续的一段时间,而每一天都依次吃早餐b、晚餐d、宵夜s,而他有可能会漏掉其中的一些餐。已知三种餐吃的次数,求最少漏了多少餐。

    题解:可以分多种情况讨论,得到一个贪心:

    假设他是从b开始吃,最后一餐吃b,则b会比另外两种多1。

    假设他是从b开始吃,最后一餐吃d,则b和d会比s多1。

    假设……

    最后会发现一个规律,要把最少的两种都配到最多比最多的一种少1,而和他们的顺序无关(三种是完全对称的)。

    *D - Exams

    挺有意思的一道题。

    题意:一段连续的日子,每个日子可能会有恰好一门课的Exam,或者没有任何Exam。问这段日子是否可以成功通过所有课的考试。注意每门课可能有多次Exam,只需要选一门完成。要是不参加当天的考试,则可以用来复习。第j门课在考试前需要aj天复习,问最短的通过所有考试的时间,或说明无解。

    题解:一开始还在想怎么用堆(延迟决策贪心)来贪心,但是显然有个二分的算法,二分天数x,则每门课肯定选x及其之前的最后一次考试来考,这个可以O(n)得到,然后必定是贪心,到了选择的一门课的时候就要尽可能分配已有的复习资源给它,不够分配则GG。

    int n, m;
    int d[100005];
    int a[100005];
    
    pii lst[100005];
    
    bool check(int len) {
        for(int i = 1; i <= m; ++i)
            lst[i] = {0, a[i]};
        for(int i = 1; i <= len; ++i)
            lst[d[i]].first = i;
        for(int i = 1; i <= m; ++i) {
            if(lst[i].first == 0)
                return 0;
        }
        sort(lst + 1, lst + 1 + m);
        int cnt0 = lst[1].first - 1;
        for(int i = 1; i <= m; ++i) {
            if(cnt0 < lst[i].second)
                return 0;
            else {
                if(i == m)
                    return 1;
                cnt0 -= lst[i].second;
                cnt0 += (lst[i + 1].first - lst[i].first - 1);
            }
        }
        return 0;
    }
    
    int LB;
    int bs() {
        int L = LB, R = n;
        while(1) {
            int M = (L + R) >> 1;
            if(L == M) {
                if(check(L))
                    return L;
                if(check(R))
                    return R;
                return -1;
            }
            if(check(M))
                R = M;
            else
                L = M + 1;
        }
    }
    
    bool vis[100005];
    
    void test_case() {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &d[i]);
        int cnt = 0;
        LB = -1;
        for(int i = 1; i <= n; ++i) {
            if(!vis[d[i]]) {
                vis[d[i]] = 1;
                ++cnt;
                if(cnt == m) {
                    LB = i;
                    break;
                }
            }
        }
        if(LB == -1) {
            puts("-1");
            return;
        }
        for(int i = 1; i <= m; ++i)
            scanf("%d", &a[i]);
        printf("%d
    ", bs());
    }
    

    *E - Sockets

    题意:有 (n(1leq n leq 2cdot 10^5)) 电脑, (m(1leq m leq 2cdot 10^5)) 个插座,其中每个电脑和插座都有自己唯一的功率。然后可以花一个适配器把一个插座的功率变成它的一半的上整(为什么是上整,不是很明白)。匹配尽可能多的电脑,在此前提上使用尽可能少的适配器。

    题解:比赛时有个假算法,感觉比题目要求的复杂度略高。把所有电脑和插座都放在map里,键是功率,值是序号数组。显然在可以匹配的时候就匹配肯定是最优的,因为留着电脑只会在下次匹配多用适配器或者直接失配。所以每次把插座的值都减半就可以了,总共的复杂度应该是 (O(nlognlogMaxPower)) ,写了一发居然MLE了,说明map开内存实在玄学,所以后面改成值就不保存序号数组了,改为保存代表序号数组的地址。

    但是按道理把这俩直接排序之后,可以直接双指针扫描完成匹配,然后再扫一遍完成减半,还不需要再排序,所以总复杂是 (O(nlogMaxPower+nlogn))

    内存和时间都差点爆了。

    stack<int> nxtA[200005];
    stack<int> nxtB[200005];
    map<int, int> A;
    map<int, int> B;
    map<int, int> TB;
    
    int C[200005];
    int D[200005];
    
    void test_case() {
        int n, m;
        scanf("%d%d", &n, &m);
        for(int i = 1, a; i <= n; ++i) {
            scanf("%d", &a);
            if(!A.count(a))
                A[a] = i;
            nxtA[A[a]].push(i);
        }
        for(int j = 1, b; j <= m; ++j) {
            scanf("%d", &b);
            if(!B.count(b))
                B[b] = j;
            nxtB[B[b]].push(j);
        }
        int cnt = 0, sum = 0;
        int cost = 0;
        int t = 31;
        while(t--) {
            for(auto &i : B) {
                auto it = A.find(i.first);
                if(it == A.end())
                    continue;
                else {
                    stack<int> &sB = nxtB[i.second];
                    stack<int> &sA = nxtA[it->second];
                    while(sA.size() && sB.size()) {
                        int u = sB.top();
                        int v = sA.top();
                        C[u] = cost;
                        D[v] = u;
                        sum += cost;
                        ++cnt;
                        sB.pop();
                        sA.pop();
                    }
                }
            }
            ++cost;
            for(auto &i : B) {
                if(nxtB[i.second].size() == 0)
                    continue;
                auto it = TB.find((i.first + 1) / 2);
                if(it == TB.end())
                    TB[(i.first + 1) / 2] = i.second;
                else {
                    stack<int> &sB = nxtB[i.second];
                    int v = it->second;
                    while(sB.size()) {
                        nxtB[v].push(sB.top());
                        sB.pop();
                    }
                }
            }
            B.swap(TB);
            TB.clear();
        }
        printf("%d %d
    ", cnt, sum);
        for(int j = 1; j <= m; ++j)
            printf("%d%c", C[j], " 
    "[j == m]);
        for(int i = 1; i <= n; ++i)
            printf("%d%c", D[i], " 
    "[i == n]);
    }
    

    重新写一个好点的。突然发现反正复杂度都是对的,甚至没有必要删除元素,直接标记为删除就可以了。

    int n, m;
    struct AB {
        int val;
        int idx;
        int vis;
    } A[200005], B[200005];
    
    bool cmpval(const AB &c1, const AB &c2) {
        return c1.val < c2.val;
    }
    
    bool cmpidx(const AB &c1, const AB &c2) {
        return c1.idx < c2.idx;
    }
    
    void test_case() {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &A[i].val);
            A[i].idx = i;
            A[i].vis = 0;
        }
        for(int j = 1; j <= m; ++j) {
            scanf("%d", &B[j].val);
            B[j].idx = j;
            B[j].vis = 0;
        }
        sort(A + 1, A + 1 + n, cmpval);
        sort(B + 1, B + 1 + m, cmpval);
        int suc = 0, sum = 0, cnt = 0;
        while(cnt <= 30) {
            int i = 1, j = 1;
            while(1) {
                while(i <= n && A[i].vis != 0)
                    ++i;
                while(j <= m && B[j].vis != 0)
                    ++j;
                if(i > n || j > m)
                    break;
                if(A[i].val == B[j].val) {
                    sum += cnt;
                    A[i].vis = B[j].idx;
                    B[j].vis = A[i].idx;
                    B[j].val = cnt;
                    ++suc;
                    ++i;
                    ++j;
                } else {
                    if(A[i].val > B[j].val)
                        ++j;
                    else
                        ++i;
                }
            }
            for(int j = 1; j <= m; ++j) {
                if(B[j].vis == 0)
                    B[j].val = (B[j].val + 1) / 2;
            }
            ++cnt;
        }
        sort(A + 1, A + 1 + n, cmpidx);
        sort(B + 1, B + 1 + m, cmpidx);
        printf("%d %d
    ", suc, sum);
        for(int j = 1; j <= m; ++j)
            printf("%d%c", B[j].vis ? B[j].val : 0, " 
    "[j == m]);
        for(int i = 1; i <= n; ++i)
            printf("%d%c", A[i].vis, " 
    "[i == n]);
    }
    

    收获:要注意vis的值不能直接用来记录用了多少个,因为有些是用0个的。需要做一点修改。还有就是注意区分排序后的下标和原下标,题目要的肯定是原下标。

    F - Tourist Reform

  • 相关阅读:
    Class:向传统类模式转变的构造函数
    连载:面向对象葵花宝典:思想、技巧与实践(34)
    Java Web文件下载
    POJ 1469(裸二分匹配)
    查看程序占用tomcat内存情况
    《对象程序设计》课程 课程设计、考试安排 及 教师建议(2014.06.30修正)
    zoj 1880
    STM8S PWM 应用 呼吸灯
    Android开发系列(二十四):Notification的功能与使用方法
    HDU 4499 Cannon (暴力搜索)
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/12285846.html
Copyright © 2011-2022 走看看