• 「2019纪中集训Day21」解题报告


    T1、数字

    \(s_i\) 表示将 \(1\)\(i\) 依次相连得到的字符串;
    \(T \ (T \leq 10 ^ 4)\) 组询问,每次输出使 \(n \ (n \leq 10 ^{18})\) 作为子串出现在 \(s_x\) 中最小的 \(x\)

    \(Sol\)

    傻逼题
    只有三种情况:
    1、\(x = n\)
    2、\(n\) 被分成两部分,前半部分为 \(x - 1\) 的后缀,后半部分为 \(x\) 的前缀;
    3、\(n\) 被分成若干部分,存在数完整地出现在 \(n\) 中。
    喜闻乐见大模拟,代码十分不可读。
    可读代码详见 这里

    时间复杂度 \(O(T \log_{10} ^ 3 n)\)

    $Source:

    #include <cstdio>
    #include <algorithm>
    int in() {
        int x = 0; char c = getchar(); bool f = 0;
        while (c < '0' || c > '9')
            f |= c == '-', c = getchar();
        while (c >= '0' && c <= '9')
            x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
        return f ? -x : x;
    }
    long long lin() {
        long long x = 0; char c = getchar(); bool f = 0;
        while (c < '0' || c > '9')
            f |= c == '-', c = getchar();
        while (c >= '0' && c <= '9')
            x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
        return f ? -x : x;
    }
    template<typename T>inline void chk_min(T &_, T __) { _ = _ < __ ? _ : __; }
    template<typename T>inline void chk_max(T &_, T __) { _ = _ > __ ? _ : __; }
    
    long long n, Pow[105];
    int nn;
    
    //Devide into 3 parts begin
    long long calc(const int len) {
        int sub_len;
        bool flag;
        long long tmp, min = 1ll << 60ll;
        for (int i = nn, j, k; i + len > nn && i - len >= 0; --i) {
            long long now = n / Pow[nn - i] % Pow[len];
            tmp = now;
            flag = 0;
            if (i != nn) {
                ++tmp;
                sub_len = len + (tmp == Pow[len]);
                if (!(n % Pow[nn - i] / Pow[nn - i - 1]) || tmp / Pow[sub_len - (nn - i)] != n % Pow[nn - i])
                    flag = 1;
            }
            if (flag == 1)
                continue;
            j = i, k = len;
            while (now && j - k > 0) {
                if (!(now / Pow[k - 1]) || now != n / Pow[nn - j] % Pow[k]) {
                    flag = 1;
                    break;
                }
                j -= k;
                if (now == Pow[k - 1])
                    --k;
                --now;
            }
            if (j) {
                if (n / Pow[nn - j] != now % Pow[j])
                    flag = 1;
            }
            if (!flag) {
                chk_min(min, tmp);
            }
        }
        return min;
    }
    //Devide into 3 parts end
    
    int main() {
        //freopen("in", "r", stdin);
        freopen("number.in", "r", stdin);
        freopen("number.out", "w", stdout);
        int T = in();
        long long res;
        Pow[0] = 1;
        for (int i = 1; i <= 17; ++i)
            Pow[i] = Pow[i - 1] * 10;
        while (T--) {
            long long tmp;
            n = res = tmp = lin(), nn = 0;
            while (tmp)
                tmp /= 10, ++nn;
            tmp = n;
            //Devide into 2 parts begin
            for (int i = 1; i < nn; ++i) {
                if (n % Pow[nn - i] / Pow[nn - i - 1]) {
                    for (int j = std::max(i, nn - i); j < nn; ++j) {
                        if (n % Pow[nn - j] == (n / Pow[nn - i] + 1) % Pow[i] / Pow[i - (nn - j)]) {
                            chk_min(res, (n / Pow[nn - i] + 1) % Pow[i] + n % Pow[nn - i] / Pow[nn - j] * Pow[i]);
                        }
                    }
                    chk_min(res, (n / Pow[nn - i] + 1) % Pow[i] + n % Pow[nn - i] * Pow[i]);
                }
            }
            //Devide into 2 parts end
            for (int i = 1; tmp; ++i, tmp /= 10) {
                chk_min(res, calc(i));
            }
            printf("%lld\n", res);
        }
        return 0;
    }
    

    T2、Maja

    题目链接

    给定一个 \(n \times m \ (n, m \leq 100)\) 的四联通网格,到达 \((i, j)\) 可以获得 \(C_{i,j} \ (0 \leq C_{i,j} \leq 10 ^ 9)\) 的得分 (可重复获得);
    给定起点为 \((A, B) \ (1 \leq A \leq n, 1 \leq B \leq m)\),求恰好走 \(k \ (k \leq 10 ^ 9)\) 步后回到起点的最大得分。
    保证 \(2 | k\)\(C_{i,j} = 0\)

    \(Sol\)

    由于 \(k\) 很大,最优解一定是走过一段路径后一直重复走一个循环;
    可以证明重复路径一定是相邻的两个格子;
    \(prf\)
    对于任意一段重复路径,一定可以找到两个格子的得分平均值不小于一整段重复路径的平均值。
    \(Q.E.D.\)

    \(f_{i, x, y}\) 表示走 \(i\) 步到达 \((x,y)\) 的最大价值,再在与之四联通的格子中选择得分最高的走 $\frac { k - 2i }{2} $ 次循环;
    \(i\) 最多到 \(\min \{nm, \frac{k}{2} \}\)

    时间复杂度 \(O(n ^ 2 m ^ 2)\)

    \(Source\)

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    int in() {
        int x = 0; char c = getchar(); bool f = 0;
        while (c < '0' || c > '9')
            f |= c == '-', c = getchar();
        while (c >= '0' && c <= '9')
            x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
        return f ? -x : x;
    }
    template<typename T>inline void chk_min(T &_, T __) { _ = _ < __ ? _ : __; }
    template<typename T>inline void chk_max(T &_, T __) { _ = _ > __ ? _ : __; }
    
    const int N = 105;
    int n, m, A, B, k;
    int mp[N][N];
    long long f[2][N][N], res;
    
    void work() {
        int cur = 0, lim = std::min(n * m, k >> 1);
        memset(f[0], -1, sizeof(f[0]));
        f[0][A][B] = 0;
        for (int i = 0; i < lim; ++i, cur ^= 1) {
            memset(f[cur ^ 1], -1, sizeof(f[cur ^ 1]));
            for (int x = 1; x <= n; ++x) {
                for (int y = 1; y <= m; ++y) {
                    if (!~f[cur][x][y])
                        continue;
                    if (i + i < k) {
                        int max = -1;
                        chk_max(max, mp[x + 1][y]);
                        chk_max(max, mp[x - 1][y]);
                        chk_max(max, mp[x][y + 1]);
                        chk_max(max, mp[x][y - 1]);
                        chk_max(res, f[cur][x][y] + f[cur][x][y] - mp[x][y] + 1ll * (k - i - i) / 2 * (mp[x][y] + max));
                    }
                    if (x < n)
                        chk_max(f[cur ^ 1][x + 1][y], f[cur][x][y] + mp[x + 1][y]);
                    if (x)
                        chk_max(f[cur ^ 1][x - 1][y], f[cur][x][y] + mp[x - 1][y]);
                    if (y < m)
                        chk_max(f[cur ^ 1][x][y + 1], f[cur][x][y] + mp[x][y + 1]);
                    if (y)
                        chk_max(f[cur ^ 1][x][y - 1], f[cur][x][y] + mp[x][y - 1]);
                }
            }
        }
        if (lim + lim <= k) {
            for (int x = 1; x <= n; ++x)
                for (int y = 1; y <= m; ++y)
                    if (~f[cur][x][y])
                        chk_max(res, f[cur][x][y] + f[cur][x][y] - mp[x][y]);
        }
        printf("%lld\n", res);
    }
    
    int main() {
        //freopen("in", "r", stdin);
        freopen("maja.in", "r", stdin);
        freopen("maja.out", "w", stdout);
        n = in(), m = in(), A = in(), B = in(), k = in();
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j)
                mp[i][j] = in();
        work();
        return 0;
    }
    

    T3、djq的朋友圈

    \(n \ (n \leq 42)\) 个人,编号为 \(1\) ~ \(n\),关系分 \(0、1\) 两种,已知 \(m\) 组关系 (两个人不会的关系不会给出两次),想知道 \(1\) 与其他人的关系;
    假设有一个人编号为 \(x\)
    \(1\) 与他关系已知,那么两者关系不会改变 (关系是双向的);
    否则 \(1\) 以某种顺序列举和他关系已知的人;
    假设第一个 \(1\)\(x\) 同时认识的人是 \(y\),那么 \(1\)\(x\) 的关系为 \((1,y) \ xor \ (x, y)\),其中,\((x, y)\) 表示 \(x, y\) 之间的关系类型;
    新确定的关系不会影响其他人关系的确定。
    求一种列举与 \(1\) 关系已知的人的顺序,使得与 \(1\) 关系为 \(0\) 的人数最多,输出人数。

    \(Sol\)

    将一开始与 \(1\) 关系为 \(0\) 的人设为 \(A\),与 \(1\) 不认识但与 \(A\) 中某些人认识的人设为 \(B\),显然其他人都不用考虑。
    \(|A| \leq 20\),可以将 \(A\) 状压,记 \(f_i\) 表示 \(i\) 这些人已经出现在列表上的最优解,枚举下一个出现的人即可。
    否则,将 \(B\) 状压,记 \(f_i\) 表示 \(i\) 这些人已经确定关系的最优解,转移同上。

    时间复杂度 \(O(2 ^ {\frac{n}{2}} n ^ 2)\)

    \(Source\)

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    int in() {
        int x = 0; char c = getchar(); bool f = 0;
        while (c < '0' || c > '9')
            f |= c == '-', c = getchar();
        while (c >= '0' && c <= '9')
            x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
        return f ? -x : x;
    }
    template<typename T>inline void chk_min(T &_, T __) { _ = _ < __ ? _ : __; }
    template<typename T>inline void chk_max(T &_, T __) { _ = _ > __ ? _ : __; }
    
    const int N = 51;
    
    int n, m, cnt_a, cnt_b, tot_st, t[N][N], mp[N], id[N], f[1 << 21 | 1];
    long long fri[N], agn[N];
    
    int bit_cnt(long long x, int ret = 0) {
        for (; x; x &= (x - 1), ++ret);
        return ret;
    }
    
    namespace A {
        void work() {
            tot_st = (1 << cnt_a) - 1;
            for (int i = 2; i <= n; ++i)
                if (~t[1][i])
                    id[mp[i]] = i;
            for (int i = 0; i < tot_st; ++i) {
                long long now = 0;
                for (int j = 1; j <= cnt_a; ++j)
                    if (i & (1 << (j - 1)))
                        now |= (fri[id[j]] | agn[id[j]]);
                for (int j = 1; j <= cnt_a; ++j)
                    if (~i & (1 << (j - 1))) {
                        if (!t[1][id[j]]) {
                            chk_max(f[i | (1 << (j - 1))], f[i] + bit_cnt(~now & fri[id[j]]));
                        } else {
                            chk_max(f[i | (1 << (j - 1))], f[i] + bit_cnt(~now & agn[id[j]]));
                        }
                    }
            }
            for (int i = 1; i <= cnt_a; ++i)
                f[tot_st] += (t[1][id[i]] == 0);
            printf("%d\n", f[tot_st]);
        }
    }
    
    namespace B {
        void work() {
            tot_st = (1 << cnt_b) - 1;
            for (int i = 2; i <= n; ++i)
                if (~t[1][i])
                    id[mp[i]] = i;
            for (int i = 0; i < tot_st; ++i) {
                for (int j = 1; j <= cnt_a; ++j)
                    if (~i & (fri[id[j]] | agn[id[j]])) {
                        if (!t[1][id[j]]) {
                            chk_max(f[i | fri[id[j]] | agn[id[j]]], f[i] + bit_cnt(~i & fri[id[j]]));
                        } else {
                            chk_max(f[i | fri[id[j]] | agn[id[j]]], f[i] + bit_cnt(~i & agn[id[j]]));
                        }
                    }
            }
            for (int i = 1; i <= cnt_a; ++i)
                f[tot_st] += (t[1][id[i]] == 0);
            printf("%d\n", f[tot_st]);
        }
    }
    
    int main() {
        //freopen("in", "r", stdin);
        freopen("friends.in", "r", stdin);
        freopen("friends.out", "w", stdout);
        n = in(), m = in();
        memset(t, -1, sizeof(t));
        for (int i = 1, x, y; i <= m; ++i) {
            x = in(), y = in();
            t[x][y] = t[y][x] = in();
        }
    
        for (int i = 2; i <= n; ++i) {
            if (!~t[1][i])
                continue;
            mp[i] = ++cnt_a;
            for (int j = 2; j <= n; ++j) {
                if (!~t[1][j] && ~t[i][j]) {
                    if (!mp[j])
                        mp[j] = ++cnt_b;
                    if (!t[i][j])
                        fri[i] |= (1ll << (mp[j] - 1));
                    else
                        agn[i] |= (1ll << (mp[j] - 1));
                }
            }
        }
    
        if (cnt_a <= 20) {
            A::work();
        } else {
            B::work();
        }
        return 0;
    }
    
  • 相关阅读:
    暑假集训Day1 整数划分
    暑假集训day1 水题 乘法最大
    暑假集训Day0
    【不知道什么专题】——历年几道难题的分析。
    开发语言之我见
    选择器IDEA Maven不见了
    js 里函数调用之call
    js 闭包 匿名函数 JavaScript的IIFE(即时执行方法)immediately-invoked function expression
    ideal-check项目
    浏览器内部工作原理
  • 原文地址:https://www.cnblogs.com/15owzLy1/p/11391502.html
Copyright © 2020-2023  润新知