zoukankan      html  css  js  c++  java
  • AtCoder Grand Contest 003 CDEF

    AGC003

    C - BBuBBBlesort!

    翻转相邻三个相当于交换 (a_i,a_{i+2})。离散化把值域弄到 ([1,n]),用尽量少的操作一使奇数在奇数位,偶数在偶数位,然后使用操作二一定可以让序列归位

    每次操作一都可以让两个数奇偶归位,因为一定可以先通过操作二使一奇一偶相邻,然后让操作一产生 (2) 的贡献。统计一下 (/2) 就是答案

    #include <bits/stdc++.h>
    using namespace std;
    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 = 1e5 + 5;
    int n, a[N], b[N];
    signed main() {
        read (n);
        for (int i = 1; i <= n; ++i) read (a[i]), b[i] = a[i];
        sort (b + 1, b + n + 1);
        for (int i = 1; i <= n; ++i)
            a[i] = lower_bound (b + 1, b + n + 1, a[i]) - b;
        int tmp = 0;
        for (int i = 1; i <= n; ++i)
            if ((a[i] + i) & 1) ++tmp;
        cout << tmp / 2 << endl;
        return 0;
    }
    

    D - Anticube

    分类题。如果按照常规方法 (sqrt{A_i}) 分解质因数大概率会 T,在 (sqrt[3]{A_i}) 的范围内分解质因数成了不错的选择,复杂度不会过高,情况也不会很复杂。大致可以把数分为 (4) 类:

    1、形如 (x^3),这样的数只能选一个

    2、分解质因数后剩下的数为 (tmp)(tmp= x)

    3、(tmp = x^2)

    4、(tmp = 1)

    第二类只能和第三类配对,第四类内部配对。对于两个矛盾的数,选个数较多的。(map) 搞一搞

    #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 = 1e5 + 5, M = 1e10;
    int n, mx, a[N], m, p[N], k[N];
    void getprime () {
        for (int i = 2; i <= mx; ++i) {
            if (!k[i]) p[++m] = i;
            for (int j = 1; j <= m && p[j] * i <= mx; ++j) {
                k[i * p[j]] = 1; if (i % p[j] == 0) break;
            }
        }
    }
    unordered_map<int, int> is, one, cnt, pp, qq; int res = 0, e = 0;
    #define fi first
    #define se second
    signed main() {
        read (n);
        for (int i = 1; i <= n; ++i) read (a[i]);
        while (mx * mx * mx <= M) ++mx;
        getprime ();
        // for (int i = 1; i <= m; ++i) printf ("%lld
    ", p[i]);
        for (int i = 1; i <= mx; ++i) is[i * i * i] = 1;
        for (int i = 1; i <= n; ++i) {
            if (is[a[i]]) { e |= 1; continue; }
            int tmp = 1, qwq = 1, now = a[i], t, tag = 0;
            for (int j = 1; j <= m && p[j] <= now; ++j) {
                t = 0;
                while (now % p[j] == 0) now /= p[j], ++t; t %= 3;
                for (int q = 1; q <= t; ++q) qwq *= p[j];
                t = (3 - t) % 3;
                for (int q = 1; q <= t; ++q) {
                    tmp *= p[j]; if (tmp > M) { tag = 1; break; }
                }
                if (tag) break;
            }
            if (tag) ++res;
            else {
                if (now == 1) ++one[tmp], ++cnt[qwq];
                else {
                    int sq = sqrt (now);
                    if (sq * sq == now) ++pp[tmp * sq];
                    else ++qq[qwq * now];
                }
            }
        }
        int s = 0, ss = 0;
        for (auto i : one) {
            if (i.se > cnt[i.fi]) s += i.se;
            if (i.se == cnt[i.fi]) ss += i.se;
        }
        res += s + ss / 2;
        for (auto i : pp) if (i.se >= qq[i.fi]) res += i.se;
        for (auto i : qq) if (i.se > pp[i.fi]) res += i.se;
        printf ("%lld
    ", res + e);
        return 0;
    }
    

    E - Sequential operations on Sequence

    一串下降的数只有最后一次有用,用单调栈维护序列 (a) 单调上升。之后的做法就比较神奇了

    (i) 次形成的数组是 (i-1) 次的复制 (frac{a_i}{a_{i-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 = 1e5 + 5;
    int n, m, q, a[N], f[N], d[N];
    void work (int s, int c) {
        if (!s) return;
        int w = upper_bound (a + 1, a + m + 1, s) - a - 1;
        if (!w) d[1] += c, d[s + 1] -= c;
        else f[w] += s / a[w] * c, work (s % a[w], c);
    }
    signed main() {
        read (n), read (q);
        a[++m] = n;
        for (int i = 1, x; i <= q; ++i) {
            read (x);
            while (x < a[m] && m) --m;
            a[++m] = x;
        }
        f[m] = 1;
        for (int i = m; i > 1; --i)
            f[i - 1] += a[i] / a[i - 1] * f[i], work (a[i] % a[i - 1], f[i]);
        d[1] += f[1], d[a[1] + 1] -= f[1];
        for (int i = 1; i <= n; ++i) printf ("%lld
    ", d[i] += d[i - 1]);
        return 0;
    }
    

    F - Fraction of Fractal

    这个题看题解可能还是自己想来的快。

    如果两个一级分形上下叠在一起,边界上能连通,左右叠在一起也可以,记为类型 (1)

    如果只有左后或上下一种可以,记为类型 (2)

    如果都不行,记为类型 (3)

    对于类型 (1),无论怎样展开都是全部连通的,直接输出 (1)

    对于类型 (3) ,答案就是 (k-1) 级分形中黑格的数量,即 (cnt^{k-1})。因为最后一次展开后有 (cnt^{k-1}) 个一级分形,这些一级分形相互之间没有通道连通,单独成为一个连通块

    (2) 是重点,以左右的情况为例(样例就是一个左右的类型 (2)),有了上面两种分析,可以发现答案是:(k-1) 级分形中黑格的数量减去左右相邻的黑格对数量。

    只要求出左右相邻的黑格对数就 (ok) 了。

    (k) 的范围很大,莫非这就是一个线性递推式的矩阵乘法优化?

    猜对了!

    先定义变量(和代码一致):(ans),当前左右相邻的黑格对数量;(cnt),一级分形中黑格数量;(num’),当前分形左右摆放后边界处相连的行数;(tot),一级分形中左右相邻黑格对数量;(num),一级分形左右摆放后边界处相连的行数。样例中 (cnt=6,tot=2,num=2)

    有递推式 (ans = ans imes cnt + num' imes tot, num' = num' imes num)

    可以这样理解:把 (p) 级分形按照一级分形划分成 (h imes w) 块,一级分形中白色的区域全都是白色,黑色的区域是一个 (p-1) 级分形。(ans imes cnt) 表示 (cnt)(p-1) 级分形内部的答案,(num' imes tot) 表示相邻的 (p-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 = 1005, mod = 1e9 + 7;
    int n, m, k, cnt, tot, num; char a[N][N];
    int qpow (int x, int y) {
        int t = 1; while (y) { if (y & 1) t = t * x % mod; x = x * x % mod, y /= 2; } return t;
    }
    struct mat {
        int a[2][2];
        void clear () { memset (a, 0, sizeof (a)); }
        mat operator * (const mat &b) const {
            mat t; t.clear();
            for (int i = 0; i < 2; ++i)
                for (int j = 0; j < 2; ++j)
                    for (int k = 0; k < 2; ++k) (t.a[i][j] += a[i][k] * b.a[k][j]) %= mod;
            return t;
        }
    } res, u;
    signed main() {
        read (n), read (m), read (k); --k;
        for (int i = 1; i <= n; ++i) {
            scanf ("%s", a[i] + 1);
            for (int j = 1; j <= m; ++j) a[i][j] = (a[i][j] == '#'), cnt += a[i][j];
        }
        int ka = 0, kb = 0;
        for (int i = 1; i <= n; ++i) ka |= (a[i][1] & a[i][m]);
        for (int i = 1; i <= m; ++i) kb |= (a[1][i] & a[n][i]);
        if (!ka && !kb) return printf ("%lld
    ", qpow (cnt, k)), 0;
        if (ka && kb) return puts ("1"), 0;
        // ans = ans * cnt + num' * tot, num' = num' * num
        // cnt,一级分形中黑格数量;num’,当前分形左右摆放后边界处相连的行数;tot,一级分形中左右相邻黑格对数量;num,一级分形左右摆放后边界处相连的行数
        if (ka) {
            for (int i = 1; i <= n; ++i)
                for (int j = 1; j < m; ++j) tot += (a[i][j] && a[i][j + 1]);
            for (int i = 1; i <= n; ++i) num += (a[i][1] && a[i][m]);
    
        } else {
            for (int j = 1; j <= m; ++j)
                for (int i = 1; i < n; ++i) tot += (a[i][j] && a[i + 1][j]);
            for (int i = 1; i <= m; ++i) num += (a[1][i] && a[n][i]);
        }
        int ans = qpow (cnt, k);
        res.a[0][1] = 1, u.a[0][0] = cnt, u.a[1][0] = tot, u.a[1][1] = num;
        while (k) { if (k & 1) res = res * u; u = u * u, k >>= 1; }
        return printf ("%lld
    ", (ans += mod - res.a[0][0]) % mod), 0;
    }
    
    
  • 相关阅读:
    YbtOJ:NOIP2020 模拟赛B组 Day10
    洛谷11月月赛Ⅱ-div.2
    P1494 [国家集训队]小Z的袜子
    [模板]莫队/P3901 数列找不同
    P4145 上帝造题的七分钟2 / 花神游历各国
    P4109 [HEOI2015]定价
    P4168 [Violet]蒲公英
    分块
    P3378 【模板】堆(code)
    网络基础——网络层
  • 原文地址:https://www.cnblogs.com/whx666/p/14194199.html
Copyright © 2011-2022 走看看