• Codeforces Round #607 (Div. 2)


    A - Suffix Three

    题意:规定三种语言的结尾符,给出字符串,判断是什么语言。

    void test_case() {
        string s;
        cin >> s;
        reverse(s.begin(), s.end());
        if(s.substr(0, 2) == "op")
            puts("FILIPINO");
        else if(s.substr(0, 2) == "us")
            puts("JAPANESE");
        else
            puts("KOREAN");
    }
    

    B - Azamon Web Services

    题意:交换s串的最多一对字母,问是否可以严格小于c串。

    题解:找最小的那个换上来,假如有多个最小的找最后面的那个。居然越界了!

    void test_case() {
        string s, c;
        cin >> s >> c;
        int l = s.length();
        for(int i = 0; i + 1 < l; ++i) {
            int minj = i + 1;
            for(int j = i + 2; j < l; ++j) {
                if(s[j] <= s[minj])
                    minj = j;
            }
            if(s[minj] < s[i]) {
                swap(s[minj], s[i]);
                break;
            }
        }
        if(s < c)
            cout << s << endl;
        else
            cout << "---" << endl;
    }
    

    C - Cut and Paste

    题意:光标最开始在最左侧,每次操作:

    1、向右移动光标1格
    2、把光标右侧的剪切
    3、粘贴光标左侧第一个数字的次数

    求当光标移动到x<=1e6位置时的字符串的总长度。

    题解:直接模拟,超过x的字符串就不需要操作了,直接截断,但是长度继续参与运算。比赛的时候提交的那个截断长度不是x而是固定的MAXLEN=1e6,这样在超多组数据下复杂度好像不太对的,构造几个全都是3的串就复制到GG。

    const ll LMOD = 1e16;
    
    int MAXLEN;
    char s[1000005];
    
    int l;
    ll tl;
    
    void append(int x) {
        if(s[x] == '1')
            return;
        if(s[x] == '2') {
            if(l < MAXLEN) {
                int dl = l - x, ce = min(MAXLEN, l + dl);
                for(int i = x + 1; i + dl <= ce; ++i)
                    s[i + dl] = s[i];
                l += dl;
                if(l >= MAXLEN)
                    l = MAXLEN;
            }
            tl += tl - x;
            if(tl >= LMOD || tl <= -LMOD)
                tl %= MOD;
            return;
        }
        if(s[x] == '3') {
            if(l < MAXLEN) {
                int dl1 = l - x, dl2 = dl1 + dl1, ce1 = min(MAXLEN, l + dl1), ce2 = min(MAXLEN, l + dl2);
                for(int i = x + 1; i + dl1 <= ce1; ++i)
                    s[i + dl1] = s[i];
                for(int i = x + 1; i + dl2 <= ce2; ++i)
                    s[i + dl2] = s[i];
                l += dl2;
                if(l >= MAXLEN)
                    l = MAXLEN;
            }
            tl += tl - x + tl - x;
            if(tl >= LMOD || tl <= -LMOD)
                tl %= MOD;
            return;
        }
    }
    
    void test_case() {
        int c = 0;
        scanf("%d%s", &MAXLEN, s + 1);
        tl = l = strlen(s + 1);
        while(1) {
            ++c;
            append(c);
            if(c == MAXLEN)
                break;
        }
        tl = (tl % MOD + MOD) % MOD;
        printf("%lld
    ", tl);
    }
    

    D - Beingawesomeism

    题意:给一个黑白棋盘格,每次可以选择一个1*x或者x*1形状的矩形向一个方向移动,把经过的位置都涂成你选的那个矩形的样子,求最小的步数把整个棋盘都涂成黑色。

    题解:

    0、全部都是黑色
    1、有边上的一行或者一列全部都是黑色,移动这个矩形就可以
    2、可以构造出边上的一行或者一列全部都是黑色的,也就是角上的黑色或者中间的一行或者一列全部都是黑色
    3、可以构造出角上的黑色或者中间的一行或者一列全部都是黑色的,也就是边上的黑色,注意要造出中间一行或者一列也必须是边上的黑色
    4、可以构造出边上的黑色的

    
    int r, c;
    char g[205][205];
    int cntr[205], cntc[205];
    
    void test_case() {
        scanf("%d%d", &r, &c);
        for(int i = 1; i <= r; ++i)
            scanf("%s", g[i] + 1);
        for(int i = 1; i <= r; ++i)
            cntr[i] = 0;
        for(int j = 1; j <= c; ++j)
            cntc[j] = 0;
        int sum = 0;
        for(int i = 1; i <= r; ++i) {
            for(int j = 1; j <= c; ++j) {
                cntr[i] += g[i][j] == 'A';
                cntc[j] += g[i][j] == 'A';
                sum += g[i][j] == 'A';
            }
        }
        /* -1 */
        if(sum == 0) {
            puts("MORTAL");
            return;
        }
        /* 0 */
        if(sum == r * c) {
            puts("0");
            return;
        }
        /* 1 */
        if(cntr[1] == c || cntr[r] == c || cntc[1] == r || cntc[c] == r) {
            puts("1");
            return;
        }
        /* 2 */
        if(g[1][1] == 'A' || g[1][c] == 'A' || g[r][1] == 'A' || g[r][c] == 'A') {
            puts("2");
            return;
        }
        for(int i = 1; i <= r; ++i) {
            if(cntr[i] == c) {
                puts("2");
                return;
            }
        }
        for(int j = 1; j <= c; ++j) {
            if(cntc[j] == r) {
                puts("2");
                return;
            }
        }
        /* 3 */
        if(cntr[1] || cntr[r] || cntc[1] || cntc[c]) {
            puts("3");
            return;
        }
        /* 4 */
        puts("4");
        return;
    }
    

    E - Jeremy Bearimy

    题意:给一棵2k个节点的树,把k对人分在上面,每个人一个节点,使得每对人之间的距离和最大以及最小,求值。

    题解:贪心,比较某条边的两边的子树节点数量,发现最多贪的做法就是尽可能把每对人分在边的异侧,最少贪的做法就是尽可能把每对人分在同一侧。随便dfs一些就可以维护出某个点的子树,然后除去根节点以外每个点代表从根节点走向它的方向的那条边。

    vector<pair<int, int> > G[200005];
    int siz[200005], sum;
    ll GG, BB;
    
    void dfs(int u, int p, int W) {
        siz[u] = 1;
        for(auto &i : G[u]) {
            int v = i.first, w = i.second;
            if(v == p)
                continue;
            dfs(v, u, w);
            siz[u] += siz[v];
        }
        if(p != -1) {
            if(siz[u] & 1)
                BB += W;
            GG += 1ll * min(sum - siz[u], siz[u]) * W;
        }
    }
    
    void test_case() {
        int k;
        scanf("%d", &k);
        sum = 2 * k;
        for(int i = 1; i <= sum; ++i)
            G[i].clear();
        for(int i = 1; i <= sum - 1; ++i) {
            int a, b, t;
            scanf("%d%d%d", &a, &b, &t);
            G[a].push_back({b, t});
            G[b].push_back({a, t});
        }
        GG = 0, BB = 0;
        dfs(1, -1, 0);
        printf("%lld %lld
    ", BB, GG);
    }
    

    事实上vector(在多组数据的情况下因为反复申请内存)的确太慢了,明明是个O(n)的算法来的。有机会还是上链式前向星吧,顺便把图论模板里的几个vector也改成链式前向星的实现。(强连通缩点那个好像vector就很方便了,不太需要高效率)

    ll minval, maxval;
    
    struct Graph {
        static const int MAXN = 200000;
        static const int MAXM = 400000;
    
        int n;
        int head[MAXN + 5], top;
    
        struct Edge {
            int v, w, next;
        } edge[MAXM + 5];
    
        void Init(int _n) {
            n = _n;
            top = 0;
            memset(head, 0, sizeof(head[0]) * (n + 1));
        }
    
        void AddEdge(int u, int v, int w) {
            edge[++top].next = head[u];
            edge[top].v = v;
            edge[top].w = w;
            head[u] = top;
        }
    
        int siz[MAXN + 5];
        void dfs(int u, int p, int w) {
            siz[u] = 1;
            for(int i = head[u]; i; i = edge[i].next) {
                int v = edge[i].v;
                if(v == p)
                    continue;
                dfs(v, u, edge[i].w);
                siz[u] += siz[v];
            }
            if(p != -1) {
                if(siz[u] & 1)
                    minval += w;
                maxval += 1ll * min(n - siz[u], siz[u]) * w;
            }
        }
    } G;
    
    void test_case() {
        int n, k;
        scanf("%d", &k);
        n = 2 * k;
        G.Init(n);
        for(int i = 1; i <= n - 1; ++i) {
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            G.AddEdge(u, v, w);
            G.AddEdge(v, u, w);
        }
        minval = 0, maxval = 0;
        G.dfs(1, -1, 0);
        printf("%lld %lld
    ", minval, maxval);
    }
    
  • 相关阅读:
    tyvj1117 拯救ice-cream
    codevs3410 别墅房间
    codevs1099 字串变换
    codevs1226 倒水问题
    codevs2449 骑士精神
    codevs1225 八数码难题
    Wikioi 3776 生活大爆炸版石头剪子布
    codevs1197 Vigenère密码
    枚举 + exgcd
    C++ 排序引用的优化
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/12045566.html
Copyright © 2020-2023  润新知