• Codeforces Round #646 (Div. 2)


    康复场1。

    https://codeforces.com/contest/1363

    A - Odd Selection

    题意:问是否能在给出的 (n) 个数中选恰好 (x) 个数,使得他们的和为奇数。

    题解:必须选择奇数个奇数,然后不用思考这么复杂,枚举选择 ([1,x]) 个奇数是否可行,即可。

    int n, x;
    
    void TestCase() {
        scanf("%d%d", &n, &x);
        int odd = 0, even = 0;
        for(int i = 1; i <= n; ++i) {
            int ai;
            scanf("%d", &ai);
            if(ai % 2 == 1) {
                ++odd;
            } else {
                ++even;
            }
        }
        bool suc = 0;
        for(int i = 1; i <= x; i += 2) {
            if(i > odd)
                break;
            if(x - i > even)
                continue;
            suc = 1;
            break;
        }
        if(suc) {
            puts("Yes");
        } else {
            puts("No");
        }
        return;
    }
    

    想预处理之后 (O(1)) 来做,就要先使用(不超过x的)奇数个奇数,然后尽可能使用偶数。最后还要验证这些和确实是奇数才行(有可能使用了)

    B - Subsequence Hate

    题意:给一个01串,每次操作可以翻转一个位置,求至少多少次操作才能使得给出的01串中不含子序列"010"也不含子序列"101"。

    题解:构造出的串必须是先0后1或者先1后0,搞个前缀和和后缀和统计就行。

    int n;
    char s[1005];
    
    void TestCase() {
        scanf("%s", s + 1);
        n = strlen(s + 1);
        int sum0 = 0;
        int sum1 = 0;
        for(int i = 1; i <= n; ++i) {
            sum0 += (s[i] == '0');
            sum1 += (s[i] == '1');
        }
        int ans = sum0;
        int cnt0 = 0;
        int cnt1 = 0;
        for(int i = 1; i <= n; ++i) {
            sum0 -= (s[i] == '0');
            sum1 -= (s[i] == '1');
            cnt0 += (s[i] == '0');
            cnt1 += (s[i] == '1');
            ans = min(ans, cnt0 + sum1);
            ans = min(ans, cnt1 + sum0);
        }
        printf("%d
    ", ans);
        return;
    }
    

    C - Game On Leaves

    题意:给出一棵 (n) 个点的无根树和一个节点 (x) ,两个人玩游戏,轮流操作。每次操作可以选择一个叶子(度数不超过1的节点)去除,谁去除了节点 (x) 谁就获胜,问最优策略下谁赢。

    题解:若 (x) 是叶子,直接去除,先手赢,否则必须要经过一个状态(因为最优策略下没有人会先把 (x) 变成叶子,所以总是保留至少度数为2),就是 text P - X - P ,这个状态是后手必胜,然后求出初始状态里这个状态相差的节点个数的奇偶性就可以了。

    D - Guess The Maximums

    题意:交互题,题意太复杂见原题。

    提示:注意 (S_i) 的并集未必是 ([1,n])

    题解:看到这个12,大概都会往折半去想,有一个办法就是先用1次确定全集的最大值是多少,然后用至多10次来确定这个最大值在哪里(最坏情况下每次询问的长度是500-250-125-63-32-16-8-4-2-1),然后知道最大值的位置之后,找出最大值所在的 (S_i) (假如有的话),然后把其他的合并再询问最后一次。

    int n, k;
    int color[1005];
     
    int maxnum, maxidx;
     
    int query(int L, int R) {
        printf("? %d", R - L + 1);
        for(int i = L; i <= R; ++i) {
            printf(" %d", i);
        }
        printf("
    ");
        fflush(stdout);
        int x;
        scanf("%d", &x);
        assert(1 <= x && x <= n);
        return x;
    }
     
    bool check(int L, int R) {
        return query(L, R) == maxnum;
    }
     
    vector<int> tmp;
     
    void TestCase() {
        scanf("%d%d", &n, &k);
        memset(color, -1, sizeof(color));
        for(int i = 1; i <= k; ++i) {
            int s;
            scanf("%d", &s);
            for(int j = 1; j <= s; ++j) {
                int x;
                scanf("%d", &x);
                color[x] = i;
            }
        }
        maxnum = query(1, n);
        int L = 1, R = n, M;
        while(1) {
            M = (L + R) / 2;
            if(L == M) {
                if(check(L, M)) {
                    maxidx = L;
                } else {
                    maxidx = R;
                }
                break;
            }
            if(check(L, M)) {
                R = M;
            } else {
                L = M + 1;
            }
        }
        tmp.clear();
        for(int i = 1; i <= n; ++i) {
            if(color[i] != color[maxidx]) {
                tmp.push_back(i);
            }
        }
        if(color[maxidx] == -1) {
            printf("!");
            for(int i = 1; i <= k; ++i) {
                printf(" %d", maxnum);
            }
            printf("
    ");
            fflush(stdout);
            char s[20];
            scanf("%s", s + 1);
            assert(s[1] == 'C');
            return;
        }
        printf("? %d", (int)tmp.size());
        for(auto &v : tmp) {
            printf(" %d", v);
        }
        printf("
    ");
        fflush(stdout);
        int x;
        scanf("%d", &x);
        assert(1 <= x && x <= n);
     
        printf("!");
        for(int i = 1; i < color[maxidx]; ++i) {
            printf(" %d", maxnum);
        }
        printf(" %d", x);
        for(int i = color[maxidx] + 1; i <= k; ++i) {
            printf(" %d", maxnum);
        }
        printf("
    ");
        fflush(stdout);
        char s[20];
        scanf("%s", s + 1);
        assert(s[1] == 'C');
        return;
    }
    

    *E - Tree Shuffling

    题意:给出一棵 (n) 个点的有根树,根是1号点。每个节点有三个值 (a,b,c)(a) 表示这个节点的操作代价, (b) 表示节点的初始状态(只能是0或1), (c) 表示节点的目标状态(只能是0或1)。每次操作可以选择一个节点,然后选择其子树中的任意个节点,然后把这些节点的值交换到你想要的样子,求把整棵树变成目标状态的最小代价。

    题解:一开始想了一个树dp,是错的,这个树dp是 (dp[u]) 表示节点 (u) 的子树复原的最小代价,然后若不能复原则为无穷。若 (u) 可以复原,则 (dp[u]=sumlimits_{vin son(u)}min(dp[v],a[u]*dif[v])) ,其中 (dif[v]) 表示节点 (v) 中初始状态和目标状态不同的数量。后来打了一个 (a[u]=min(a[u],a[p])) 的补丁,还是错的。错在一棵树就算没办法复原也可以尽可能复原(利用子树中的低代价来复原大部分,只留下一种不能复原的)。

    int n, x;
    int a[200005];
    int b[200005];
    int c[200005];
    vector<int> G[200005];
    
    int siz[200005];
    int cnt01[200005];
    int cnt10[200005];
    
    ll res;
    
    void dfs(int u, int p, int ap) {
        a[u] = min(a[u], ap);
        siz[u] = 1;
        cnt01[u] = (b[u] == 0 && c[u] == 1);
        cnt10[u] = (b[u] == 1 && c[u] == 0);
        for(auto &v : G[u]) {
            if(v == p)
                continue;
            dfs(v, u, a[u]);
            siz[u] += siz[v];
            cnt01[u] += cnt01[v];
            cnt10[u] += cnt10[v];
        }
        int tmp = min(cnt01[u], cnt10[u]);
        cnt01[u] -= tmp;
        cnt10[u] -= tmp;
        res += 2ll * tmp * a[u];
        return;
    }
    
    void TestCase() {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i) {
            G[i].clear();
            scanf("%d%d%d", &a[i], &b[i], &c[i]);
        }
        for(int i = 1; i <= n - 1; ++i) {
            int u, v;
            scanf("%d%d", &u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        res = 0;
        dfs(1, 0, INF);
        if(cnt01[1] || cnt10[1])
            res = -1ll;
        printf("%lld
    ", res);
        return;
    }
    
  • 相关阅读:
    【算法】百度百科经典算法链接集
    【剑指offer】38.字符串的排列
    Spring整合RabbitMQ
    JVM对象的内存分配,内存布局和访问定位
    记一次需求的表结构设计变更
    sql server中的系统数据库
    HttpWebRequest / HttpWebResponse 远程获取文件信息
    XML的操作
    对于XML无法传输转义字符的问题
    还原数备份文件 SQL语句
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/13041042.html
Copyright © 2020-2023  润新知