zoukankan      html  css  js  c++  java
  • Codeforces Round #607 (Div. 1)

    Codeforces Round #607 (Div. 1)

    A

    每个位置一旦被赋值就不会再更改。记录当前哪些位置已经赋值,然后暴力更改没赋值的位置。但 (m) 之后的用不到不用管

    B

    答案只有 (6)

    (res=0) 初始就全都是 (A)

    (res=1) 矩阵的四条边界中有某条边界全是 (A)

    (res=2) 某一行(列)全是 (A) 或者四个顶点中某个是 (A)

    (res=3) 四条边界上有格子是 (A)

    (res=impossible) 没有 (A)

    (res=4) 除上述情况外

    方案挺好构造的,自己 yy 吧

    C

    对每条边分开考虑,设这条边断开后树的两部分大小分别为 (x,y)

    1、取最大值的时候这条边算了 (min(x,y))

    2、对于最小值,如果这条边经过了 (ge 2) 次,这些点对为 ((x_1,y_1),(x_2,y_2)...) 其中 (x_i) 属于同一部分,(y_i) 属于另一部分。那么不妨将 (x_i,y_i) 分别两两配对,更改为 ((x_1,x_2),(x_3,x_4)...(y_1,y_2),(y_3,y_4)...)。这样更改后经过这条边的次数减少了,对于其他边也不会更劣。由此得出,如果 (x\%2=1),要算 (1) 次,否则经过 (0)

    D

    (f_{i,j}) 表示以 (i) 为根的子树中,分了 (j) 块时,符合条件的块数最大值(i) 所在块当前 (w)(b) 差的最大值 (这是一个 (pair)

    也就是说,(dp) 的时候用了一个贪心。可以这样理解,有两个最优条件:1、符合条件块数最多 2、当前差值最大

    很明显,(1) 的优先级高于 (2),因为差值再大也只能把块数 (+1),所以先让 (1) 最优后再考虑 (2)

    转移的时候就和树上背包一样枚举父亲和儿子的 (j)(第二维),分两种情况(父亲和儿子在不在一个块内)讨论,复杂度 (O(n^2))

    #include <bits/stdc++.h>
    using namespace std;
    #define int long long
    void read (int &x) {
        char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
        while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
    } const int N = 3005, M = N << 1;
    int n, m, a[N], b[N];
    int cnt, h[N], nxt[M], to[M], sz[N];
    void add (int u, int v) {
        to[++cnt] = v, nxt[cnt] = h[u], h[u] = cnt;
    }
    pair<int, int> f[N][N], t[N];
    #define fi first
    #define se second
    void dfs (int u, int la) {
        sz[u] = 1; f[u][1] = {0, a[u]};
        for (int i = h[u], v; i; i = nxt[i]) {
            if ((v = to[i]) == la) continue;
            dfs (v, u);
            for (int j = 1; j <= sz[u] + sz[v]; ++j) t[j] = {-1, 0}; // 弄成-1因为有些状态不可达
            for (int j = 1; j <= sz[u]; ++j)
                for (int k = 1; k <= sz[v]; ++k) {
                    t[j + k] = max (t[j + k], {f[v][k].fi + f[u][j].fi + (f[v][k].se > 0), f[u][j].se});
                    t[j + k - 1] = max (t[j + k - 1], {f[v][k].fi + f[u][j].fi, f[v][k].se + f[u][j].se});
                }
            sz[u] += sz[v];
            for (int j = 1; j <= sz[u]; ++j) f[u][j] = t[j];
        }
    }
    signed main() {
        int T; read (T);
        while (T--) {
            read (n), read (m); cnt = 0;
            for (int i = 1; i <= n; ++i) h[i] = 0;
            for (int i = 1; i <= n; ++i)
                for (int j = 1; j <= m; ++j) f[i][j] = {0, 0};
            for (int i = 1; i <= n; ++i) read (b[i]);
            for (int i = 1; i <= n; ++i) read (a[i]), a[i] -= b[i];
            for (int i = 1, u, v; i < n; ++i)
                read (u), read (v), add (u, v), add (v, u);
            dfs (1, 0);
            printf ("%lld
    ", f[1][m].fi + (f[1][m].se > 0));
        }
        return 0;
    }
    

    E

    对于一个形态固定的电阻网络,设 (g(R)) 表示电路阻值为 (R) 时各个电阻阻值和的最小值。把这个带括号的序列对应到树上,要求的就是根节点的 (g) 函数关系式。更进一步,不难发现 (g) 其实是一个正比例函数,设 (f(x)) 为节点 (x) 处的 (g) 函数系数。如果没有整数的限制,考虑如何求得 (f)

    1、如果 (x) 节点的儿子是串联关系,显然,最优的办法是把所有的电阻都加在 (f) 最小的那个儿子上,其他放空。(f_x=min(f_y))

    2、如果是串联,列出两个式子,(frac{1}{R_x}=sumfrac{1}{R_y},sum=sum f_yR_y,yin son(x)),由第一个式子可得 (R_xsumfrac{1}{R_y}=1),联想到“1”的妙用,把这个东西放入第二个式子:(sum=R_xsumfrac{1}{R_y}sum f_yR_y=R_xsum(frac{1}{sqrt{R_y}})^2sum sqrt{f_yR_y}^2)。套用柯西不等式的取等条件,当取值最小时有 (frac{1}{sqrt{f_yR_y^2}}=m)(m) 为定值)。那么 (frac{1}{R_y}propto sqrt{f_y}),而 (sumfrac{1}{R_y}) 为定值,那么 (frac{1}{R_y}) 按照比例分配。可得 (R_y=R_xfrac{sumsqrt{f_z}}{sqrt{f_y}})带入可得 (sqrt{f_x}=sumsqrt{f_y})

    再回来看整数的限制

    1、对于叶子节点有 (f_x=1),是完全平方数

    2、对于第一种转移,如果 (f_y) 全是完全平方数那么 (f_x) 也一定是

    3、对于第二种转移,(f_y) 是完全平方数,(sqrt{f_y}) 是整数,(sqrt{f_x}) 是整数,(f_x) 依然是一个完全平方数

    那么,整数的限制作废了

    计算具体阻值的时候可以按照构造方法往里面带,更简洁的做法:情况 (1) 的构造方法意味着多个串联的电阻中只会选一个。根据电路分析的基础知识可以发现,最后选出的电阻都可以看作并联关系,那么看一下有几个电阻用到然后...

    #include <bits/stdc++.h>
    using namespace std;
    #define int long long
    void read (int &x) {
        char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
        while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
    } const int N = 2e5 + 5, M = 5e5 + 5;
    int n, dfn, st[N], tag[N], to[N], f[N], vis[N], id[N];
    char a[M]; vector<int> g[N];
    #define pb push_back
    void build () {
        int num = 0, tp = 0;
        for (int i = 1; i <= n; ++i) {
            if (a[i] == '(') {
                g[++num].clear();
                if (tp) g[st[tp]].pb (num);
                st[++tp] = num;
                to[num] = vis[num] = f[num] = 0;
            } else if (a[i] == '*') {
                g[++num].clear();
                if (tp) g[st[tp]].pb (num);
                to[num] = vis[num] = f[num] = 0;
            }
            else if (a[i] == 'S') tag[st[tp]] = 0;
            else if (a[i] == 'P') tag[st[tp]] = 1;
            else if (a[i] == ')') --tp;
        }
    }
    void dfs (int u) {
        if (!g[u].size()) { f[u] = 1, id[u] = ++dfn; return; }
        for (int v : g[u]) dfs (v);
        if (tag[u] == 0) { // 串联直接找系数最小的
            int mn = 0;
            for (int v : g[u]) if (f[mn] > f[v]) mn = v;
            to[u] = mn, f[u] = f[mn];
        } else { // 串联直接加和
            for (int v : g[u]) f[u] += f[v];
        }
    } int cnt;
    void getcnt (int u) {
        if (!g[u].size()) { vis[id[u]] = 1, ++cnt; return; }
        if (!tag[u] && to[u]) getcnt (to[u]);
        else for (int v : g[u]) getcnt (v);
    }
    signed main() {
        int T; read (T);
        while (T--) {
            int R; scanf ("%lld", &R);
            cin.getline (a, 5e5); n = strlen (a + 1);
            build (); f[0] = 2e9;
            dfn = 0, dfs (1);
            cnt = 0; getcnt (1);
            printf ("REVOLTING ");
            for (int i = 1; i <= dfn; ++i)
                printf ("%lld ", vis[i] ? cnt * R : 0ll);
            putchar ('
    ');
        }
        return 0;
    }
    

    F

    这个游戏可以看成有三个可以转动的环,左右分别一个小的,整体是一个大的。说“转动”是因为把 (E) 沿着环转一圈相当于把换上的数字转动一个位置

    何时无解?设 (A) 为按照行列顺次取数后得到的排列,怎样操作 (A) 的逆序对个数的奇偶性都不变,最终状态中显然没有逆序对,所以逆序对个数为奇数时无解。为偶数时如何构造答案?

    我们规定 (E) 在第二行中间的位置时为标准状态,每次转动完依旧是标准状态。现在要做的就是把逆序对个数减少为 (0),然后再把 (E) 移到最右边

    设第一行中间位置为 (W)。引进两个成套操作

    1、对于左半部分的任意位置两个点 (a,b) 和右部任意一个点 (c)

    转动左轮,使 (b)(W)

    转动右轮,使 (c)(W)

    转动左轮,使 (a)(W)

    转动右轮,使 (b)(W),此时 (a) 到达原来 (c) 的位置

    转动左轮,使 (b) 到原来 (a) 的位置,此时 (c) 一定在原来 (b) 的位置(距离相同)

    这样,((a,b,c)) 成了 ((c,a,b)),左一右二的类似

    通过这个操作,可以把应该在左部的数换到左部,该在右边的扔到右边

    2、对于左部两个点 (a,b),和右部两个点 (c,d),可以通过 (2)(1) 操作将 ((a,b)(c,d)) 变成 ((b,a)(d,c))

    通过这个操作可以消除同一个部分之间的逆序对

    具体的就不说了,咕咕咕

  • 相关阅读:
    find命令
    shell编程基础
    grep命令
    awk命令
    结对项目之需求分析与原型模型设计
    使用Git进行代码管理的心得
    软件工程的实践项目的自我目标
    第五次作业——团队项目——需求规格说明书
    调研android开发环境的发展演变
    结对编程总结
  • 原文地址:https://www.cnblogs.com/whx666/p/607-div1.html
Copyright © 2011-2022 走看看