Codeforces Round #626 (Div. 1)

Posted 敲键盘的猫

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Round #626 (Div. 1)相关的知识,希望对你有一定的参考价值。

Codeforces Round #626 (Div. 1)

A

B

对每一位分别计算,只考虑 \\(x,y\\) 的前 \\(k\\) 位,若 \\(x+y\\) 的第 \\(k\\) 位为 \\(1\\),则 \\(x+y\\in[2^k,2^{k+1})\\cup[2^k+2^{k+1},2^{k+2})\\),排序后双指针统计即可

C

结论题,但大多数题解的证明有问题,建议看官方题解或[中文版题解](题解 CF1322C [Instant Noodles] - 老年退役选手 QwQcOrZ 的小窝 - 洛谷博客 (luogu.com.cn)),享受优美严谨的证明

D

\\(f_{i,j}\\) 表示有 \\(j\\) 个等级为 \\(i\\) 的选手的最大收益(当前最大等级为 \\(i\\))。加入一个等级 \\(l_i\\) 的人有两种转移:1. 推一遍 \\(f_{l_i,*}\\);2. 把 \\(l_i\\) 推向 \\(l_{i+1},l_{i+2}\\dots\\)。在这样的转移中,如果后加入的 \\(i<j,l_j<l_i\\),则 \\(i\\) 不会影响 \\(j\\),正好与题目要求相反,所以倒着处理每个人。

转移 \\(1\\) 的时间复杂度显然是 \\(O(n^2)\\),对于转移 \\(2\\),每向上一层数量减半,复杂度为 \\(O(n\\times(n+\\frac{n}{2}+\\frac{n}{4}+\\dots))=O(n^2)\\)

int n, m, mx, res, l[N], s[N], c[N << 1], f[N << 1][N << 1];
void Max (int &a, const int b) { if (b > a) a = b; }
void modify (int p, int x) {
    int tmp = 0; while (f[p][tmp] != INT_MIN) ++tmp;
    for (int i = tmp; i >= 1; --i) Max (f[p][i], f[p][i - 1] + c[p] - x);
}
void update (int p) {
    for (int i = 0; i <= n && f[p][i] != INT_MIN; ++i)
        Max (f[p + 1][i >> 1], f[p][i] + (i >> 1) * c[p + 1]);
    if (p < n + m) update (p + 1);
}
signed main() {
    read (n), read (m);
    for (int i = 1; i <= n; ++i) read (l[i]);
    for (int i = 1; i <= n; ++i) read (s[i]);
    for (int i = 1; i <= n + m; ++i)
        for (int j = 0; j <= n * 2; ++j) f[i][j] = INT_MIN;
    for (int i = 1; i <= n + m; ++i) read (c[i]), f[i][0] = 0;
    for (int i = n; i >= 1; --i) modify (l[i], s[i]), update (l[i]);
    for (int i = 1; i <= n + m + 1; ++i) Max (res, f[i][1]);
    return printf ("%lld\\n", res), 0;
}

E

暴力做法如何确定最后的序列?枚举一个值 \\(k\\)\\(b_i=(a_i<k)\\),也就是只记录 \\(a_i\\)\\(k\\) 的大小关系。设 \\(a\',b\'\\) 为变换后最终得到的 \\(a,b\\)。取中位数的操作并不会改变这个大小关系。不难发现,一段长度 \\(\\ge2\\) 的连续的 \\(0/1\\) 段永远也不会变(称它们为 \\(0/1\\) 段),相邻的连续段之间夹了 \\(010101\\dots\\) 这样的东西,然后会从两头往中间扩散,这样就得到了 \\(b\'\\),算完所有 \\(k\\) 后也就可以得到 \\(a\'\\),而操作次数就是所有 \\(k\\) 对应的 \\(b\\) 的最长的 \\(01\\) 段长度除以 \\(2\\)\\(k\\) 取所有 \\(a_i\\),复杂度 \\(O(n^2)\\)

介绍两种优化方法。

第一种:依旧从小到大枚举 \\(k=a_i\\),此时 \\(b\\) 中有一些 \\(0\\) 变为 \\(1\\),对于每一个变化的位置,\\(b’\\) 中也有一些 \\(0\\) 变为 \\(1\\),且构成一个区间,可用 \\(set\\) 等数据结构维护,\\(O(n \\log n)\\)。但又长又慢

第二种:对单个位置 \\(x\\) 进行计算,因为只考虑一个数,所以可以二分 \\(k\\),不必枚举。剩下的问题是如何找到离 \\(x\\) 最近的连续段。令 \\(p_i=min/max(a_i,a_{i+1})\\),然后反着处理 \\(p\\) 的最大值最小值的 \\(ST\\) 表(对于 \\(min(a_i,a_{i+1})\\) 反过来处理 \\(max\\)\\(st\\)\\(MX\\),对于 \\(max(a_i,a_{i+1})\\) 处理 \\(min\\)\\(st\\)\\(MN\\)),再套个二分分别算出最近的 \\(0\\) 段和 \\(1\\) 段,即可得到最近的连续段,\\(O(n \\log^2 n)\\)。但还有更强的做法。不用二分 \\(k\\),直接二分距离 \\(d\\)。设 \\(A=MN.ask(i-d-1,i+d),B=MX.ask(i-d-1,i+d)\\)。取 \\(k=min(A,B)\\),此时 \\(B\\ge k\\),说明在距离 \\(d\\) 内已经碰到 \\(0\\) 段,\\(A\\ge k\\),说明距离 \\(d\\) 内没有 \\(1\\) 段,那么先碰到 \\(0\\) 段,\\(b\'_i=(a\'_i<k)=0\\),即 \\(a’_i\\ge k\\)。只需找到最大的 \\(k=min(A,B)\\) 就是 \\(a\'_i\\) 的值。随着 \\(d\\) 增大,\\(A\\) 单减,\\(B\\) 单增,二分交点即可求得最大值(函数不连续,不一定有真正的交点)。至于操作次数,也可以顺带着求出

int n, res, a[N], lg[N], ma[N], na[N], b[N];
struct st {
    int c[20][N];
    void work (int *a) {
        memcpy (c[0], a, (n + 1) << 2);
        for (int j = 1; j < 20; ++j)
            for (int i = 0; i + (1 << j) - 1 <= n; ++i)
                c[j][i] = min (c[j - 1][i], c[j - 1][i + (1 << (j - 1))]);
    }
    int ask (int x, int d) {
        int l = x - d - 1, r = x + d, t = lg[r - l + 1];
        return min (c[t][l], c[t][r - (1 << t) + 1]);
    }
} mx, mn;
signed main() {
    read (n); lg[0] = -1;
    for (int i = 1; i <= n + 1; ++i) lg[i] = lg[i >> 1] + 1;
    for (int i = 1; i <= n; ++i) read (a[i]);
    a[0] = a[1], a[n + 1] = a[n];
    for (int i = 0; i <= n; ++i)
        ma[i] = -min (a[i], a[i + 1]), na[i] = max (a[i], a[i + 1]);
    mn.work (na), mx.work (ma);
    for (int i = 1; i <= n; ++i) {
        int l = 0, r = min (i - 1, n - i), mid;
        int ta, tb, val = 0;
        while (l <= r) {
            mid = l + r >> 1;
            ta = mn.ask (i, mid), tb = -mx.ask (i, mid);
            val = max (val, min (ta, tb));
            ta > tb ? l = mid + 1 : r = mid - 1;
        }
        b[i] = val, res = max (res, r + 1);
    }
    printf ("%d\\n", res);
    for (int i = 1; i <= n; ++i) printf ("%d ", b[i]); puts ("");
    return 0;
}

F

。。。。。

以上是关于Codeforces Round #626 (Div. 1)的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces Round #626 (Div. 2) B. Count Subrectangles

Codeforces Round #626 (Div. 2, based on Moscow Open Olympiad in Informatics)

Codeforces Round #626 D. Present 异或按位确定 +二分or双指针

Codeforces Round #626 (Div. 2, based on Moscow Open Olympiad in Informatics)

Codeforces Round #626 (Div. 2, based on Moscow Open Olympiad in Informatics)ABCD(题解)

Codeforces Round #626 (Div. 2, based on Moscow Open Olympiad in Informatics)(A-C)