zoukankan      html  css  js  c++  java
  • Codeforces Round #591 (Div. 2, based on Technocup 2020 Elimination Round 1)

    Virtual participate 的,D题不会做,打了1:30就打不动了,过了ABCE。

    A - CME

    题意:?

    题解:?

    void test_case() {
        int n;
        scanf("%d", &n);
        if(n == 2)
            puts("2");
        else
            printf("%d
    ", n & 1);
    }
    

    B - Strings Equalization

    题意:给两个等长的字符串,每次可以把一个字母赋值给它前一个和后一个,问两个字符串能否变成相等。

    题解:判断有没有相同的字母。

    /
     
    int cnt1[26], cnt2[26];
     
    char s[2000], t[2000];
    void test_case() {
        scanf("%s%s", s + 1, t + 1);
        for(int i = 0; i < 26; ++i)
            cnt1[i] = cnt2[i] = 0;
        int l = strlen(s + 1);
        for(int i = 1; i <= l; ++i)
            cnt1[s[i] - 'a']++;
        for(int i = 1; i <= l; ++i)
            cnt2[t[i] - 'a']++;
     
        for(int i = 0; i < 26; ++i) {
            if(cnt1[i] && cnt2[i]) {
                puts("YES");
                return;
            }
        }
        puts("NO");
    }
    

    C - Save the Nature

    题意:电影院规定a的倍数的票的x%以及b的倍数的票的y%会捐赠出去,求捐赠出至少k元需要的最小的电影票数量。你可以任意排列这些电影票,而且a和b的公倍数会捐(x+y)%。

    题解:很显然的二分题,直接二分电影票的数量t,然后取前t大的电影票出来。先贪心分给公倍数,然后贪心分给比例更大的那个,最后的全部尝试分给比例小的那个。

    int n;
    int p[200005];
    int tp[200005];
    int x, a, y, b;
    ll g, k;
     
    bool check(int t) {
        ll cur = 0;
        int i = 1;
        int restg = t / g;
        int resta = t / a - restg, restb = t / b - restg;
        while(i <= t) {
            if(restg) {
                --restg;
                cur += 1ll * tp[i] * (x + y) / 100;
            } else if(resta) {
                --resta;
                cur += 1ll * tp[i] * x / 100;
            } else if(restb) {
                --restb;
                cur += 1ll * tp[i] * y / 100;
            } else
                break;
            ++i;
        }
        return cur >= k;
    }
     
    int bs() {
        int L = 1, 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;
        }
    }
     
    void test_case() {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &p[i]);
            tp[i] = p[i];
        }
        sort(tp + 1, tp + 1 + n, greater<int>());
        scanf("%d%d%d%d%lld", &x, &a, &y, &b, &k);
        if(y > x) {
            swap(x, y);
            swap(a, b);
        }
        g = 1ll * a * b / __gcd(a, b);
        printf("%d
    ", bs());
    }
    

    其实上面没有必要再开一个tp数组。而且可以求出tp数组的前缀和,这样验证就不是O(t)了而是O(1)。

    int n;
    ll p[200005];
    int x, a, y, b;
    ll g, k;
    
    bool check(int t) {
        int rg = t / g;
        int ra = t / a - rg, rb = t / b - rg;
        ll cur = (p[rg] * (x + y) + (p[rg + ra] - p[rg]) * x + (p[rg + ra + rb] - p[rg + ra]) * y);
        return cur >= k;
    }
    
    int bs() {
        int L = 1, 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;
        }
    }
    
    void test_case() {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i)
            scanf("%lld", &p[i]);
        sort(p + 1, p + 1 + n, greater<ll>());
        for(int i = 1; i <= n; ++i)
            p[i] += p[i - 1];
        scanf("%d%d%d%d%lld", &x, &a, &y, &b, &k);
        k *= 100;
        if(y > x) {
            swap(x, y);
            swap(a, b);
        }
        g = 1ll * a * b / __gcd(a, b);
        printf("%d
    ", bs());
    }
    

    D

    参考资料:https://blog.csdn.net/Dch19990825/article/details/103017653

    E - Paint the Tree

    题意:将一棵n个节点的树,边带正权,每个节点染恰好k种颜色,且每种颜色至多染2个节点,问最大权值的染色方法。

    题解:一开始往生成树那里想了,以为是先取权最大的边贪心,这个反例很好举的,怎么会这么演呢?后面想了想会不会是树形dp呢?只需要考虑儿子的颜色满了和没满两种情况,而根本不关心子树的细节。

    所以设置dp[u][0]为u节点颜色没满的以u为根的子树的最大权和,dp[u][1]为u节点的颜色满了没满的最大权和,所以dp[u][1]>=dp[u][0]。

    先特判掉没有边的情况,免得乱七八糟的。然后度为1的点也不是根节点的点就一定是叶子,叶子直接返回。

    否则,一棵树肯定是可以不取去往子树的边的,所以直接取dp[v][1]出来,同时假如取子树的边,那么值会增加:w+dp[v][0]-dp[v][1],把这个存在每层dfs中的tmp数组里,假如怕爆栈就放在tmp[u]中就可以了(这里确实会怕爆栈的问题?不太清楚vector是怎么分配空间的,分配的是堆空间还是栈空间?),这里可以不取ll,因为dp[v][0]和dp[v][1]的差应该不太会超过一条边的权(感觉是因为dp[v][1]最多就是比dp[v][0]多取了一条边罢了,不过最好下次还是要用ll)。

    对tmp数组排序。取出最大的最多k-1个更新dp[u][0]和dp[u][1],假如还有第k个可以再更新dp[u][1]。

    int n, k;
    vector<pii>G[500005];
    ll dp[500005][2];
     
    void dfs(int u, int p) {
        if(G[u].size() == 1 && p != -1) {
            //printf("dp[%d][%d]=%lld
    ", u, 0, dp[u][0]);
            //printf("dp[%d][%d]=%lld
    ", u, 1, dp[u][1]);
            return;
        }
        vector<pii>tmp;
        for(auto &e : G[u]) {
            int v = e.first, w = e.second;
            if(v == p)
                continue;
            dfs(v, u);
            dp[u][0] += dp[v][1];
            dp[u][1] += dp[v][1];
            if(w + dp[v][0] - dp[v][1] > 0)
                tmp.push_back({w + dp[v][0] - dp[v][1], v});
        }
        sort(tmp.begin(), tmp.end(), greater<pii>());
        int c = min((int)tmp.size(), k - 1);
        for(int i = 0; i < c; ++i) {
            dp[u][0] += tmp[i].first;
            dp[u][1] += tmp[i].first;
        }
        dp[u][1] += tmp.size() >= k ? tmp[k - 1].first : 0;
        //printf("dp[%d][%d]=%lld
    ", u, 0, dp[u][0]);
        //printf("dp[%d][%d]=%lld
    ", u, 1, dp[u][1]);
        assert(dp[u][1]>=dp[u][0]);
    }
     
    void test_case() {
        scanf("%d%d", &n, &k);
        if(n == 1) {
            puts("0");
            return;
        }
        for(int i = 1; i <= n; ++i) {
            dp[i][0] = 0;
            dp[i][1] = 0;
            G[i].clear();
        }
        for(int i = 1; i <= n - 1; ++i) {
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            G[u].push_back({v, w});
            G[v].push_back({u, w});
        }
        dfs(1, -1);
        printf("%lld
    ", dp[1][1]);
    }
    

    可是我为什么写不出D呢?

  • 相关阅读:
    JavaScript中this指针指向的彻底理解
    JavaScript闭包应用介绍
    Javascript代码中的骚操作
    h2database.com 高级特性
    H2Database高级特性
    BlockingQueue的使用
    谈谈java的BlockingQueue
    Java基于Socket文件传输示例
    java socket 多线程网络传输多个文件
    用JAVA捕获屏幕、屏幕录像、播放
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/12084190.html
Copyright © 2011-2022 走看看