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


    T1、Global warming

    题目链接

    给定整数 (n (n leq 2 imes 10 ^ 5))(x (x leq 10 ^ 9)),以及一个长度为 (n) 的序列 (a (a_i leq 10 ^ 9))
    你可以选择一个区间 ([l,r]),然后令 (a_i = a_i + d (i in [l,r] igcap )),其中 (d) 满足 (|d|<=x)
    要求最大化 (a) 的最长上升子序列的长度,并输出该值。

    (Sol)

    有一个比较明显的性质:
    ([l, r])(d) 不比 给 ([l, n])(d) 优;
    ([l, r])(d) 不比 给 ([1, r])(d) 优。
    而且给一个前缀减 (d) 和给一个后缀加 (d) 本质是一样的。

    预处理前后缀的 (lis),从左至右枚举后缀的起点,用权值线段树维护前缀 (lis) 长度最大值即可。

    由于权值过大,离散化后树状数组常数会小一点,但是我懒线段树也很优秀。

    时间复杂度 (O(n log_2 n + n log_2 a_i))

    (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 __) { _ = _ > __ ? _ : __; }
    inline int max(int _, int __) { return _ > __ ? _ : __; }
    
    const int N = 2e5 + 5, M = 2e9;
    int n, x, a[N], res;
    int f[N], len;
    int pre[N], suf[N];
    
    struct segment_tree {
        int t[N * 71], c[N * 71][2], rt, tot;
        inline void clear() {
            tot = 0;
        }
        inline int new_node() {
            ++tot;
            t[tot] = c[tot][0] = c[tot][1] = 0;
            return tot;
        }
        void modify(int pos, int k, int tl, int tr, int &p) {
            if (p > tot)
                p = 0;
            if (!p)
                p = new_node();
            chk_max(t[p], k);
            if (tl == tr)
                return chk_max(t[p], k);
            int mid = ((long long)tl + tr) >> 1;
            if (mid >= pos)
                modify(pos, k, tl, mid, c[p][0]);
            else
                modify(pos, k, mid + 1, tr, c[p][1]);
        }
        int query_max(int l, int r, int tl, int tr, int &p) {
            if (p > tot)
                p = 0;
            if (!p)
                return 0;
            if (l <= tl && tr <= r)
                return t[p];
            int mid = ((long long)tl + tr) >> 1;
            if (mid < l)
                return query_max(l, r, mid + 1, tr, c[p][1]);
            if (mid >= r)
                return query_max(l, r, tl, mid, c[p][0]);
            return max(query_max(l, r, tl, mid, c[p][0]),
                       query_max(l, r, mid + 1, tr, c[p][1]));
        }
    } T;
    
    void work() {
        f[len = pre[1] = 1] = a[1];
        for (int i = 2, p; i <= n; ++i) {
            p = std::lower_bound(f + 1, f + 1 + len, a[i]) - f;
            if (p == len + 1) {
                f[++len] = a[i];
            } else {
                f[p] = a[i];
            }
            pre[i] = p;
        }
    
        res = pre[n];
        T.modify(a[n], 1, -M, M, T.rt);
        len = suf[n] = 1, f[n] = a[n];
        for (int i = n - 1, p; i; --i) {
            p = std::upper_bound(f + n - len + 1, f + 1 + n, a[i]) - f - 1;
            if (p == n - len) {
                f[n - len] = a[i];
                ++len;
            } else {
                f[p] = a[i];
            }
            suf[i] = n - p + 1;
            chk_max(res, pre[i] + T.query_max(a[i] - x + 1, M, -M, M, T.rt));
            T.modify(a[i], suf[i], -M, M, T.rt);
        }
    }
    
    int main() {
        //freopen("in", "r", stdin);
        freopen("glo.in", "r", stdin);
        freopen("glo.out", "w", stdout);
        n = in(), x = in();
        for (int i = 1; i <= n; ++i)
            a[i] = in();
        work();
        printf("%d
    ", res);
        return 0;
    }
    

    T2、Mobitel

    题目链接

    给定一个 (r (r leq 300))(c (c leq 300)) 列的矩阵,每个格子里都有一个正整数。
    问如果从左上角走到右下角,且每次只能向右或向下走到相邻格子,那么使得路径上所有数的乘积不小于 (n (n leq 10 ^ 6)) 的路径有多少条?
    由于答案可能很大,所以请输出答案对 (10^9+7) 取模的结果。
    (Time Limits: 6000 ms) (Memory Limits: 64 MB)

    (Sol)

    答案等于 ( binom{r - 1 + c - 1}{c - 1}) - 乘积 (leq n - 1) 的路径数。
    显然有一个暴力 (dp)(f_{i, j, k}) 表示到 ((i, j)) 路径乘积为 (k) 的方案数,转移是显然的。

    考虑优化状态数,记 (f_{i, j, k}) 表示到 ((i, j)) 还有 (k) 可以用来被格子上的数除;
    (lfloor frac{ lfloor frac{S}{x} floor } {y} floor = lfloor frac{S}{xy} floor),所以这样是对的。

    学过数论分块的话一定知道,这样的 (k) (即 (lfloor frac{n - 1}{i} floor)) 有一个上界 (2 sqrt{n - 1})

    (prf)
    (i leq sqrt{N}) 时,(lfloor frac{N}{i} floor) 不会超过 (sqrt{N}) 种;
    (i > sqrt{N}) 时,(lfloor frac{N}{i} floor < sqrt{N}),不会超过 (sqrt{N}) 种。
    (Q.E.D.)

    时间复杂度 (O(r c sqrt{n})),空间复杂度 (O((r + c) sqrt{n}))

    (Source)

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    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 = 305, mod = 1e9 + 7;
    
    int R, C, n, mp[N][N], f[2][N + N][2005];
    int nn, num[2005], id[1000005];
    
    inline void add(int &_, int __) {
        _ += __, _ = _ < mod ? _ : _ - mod;
    }
    
    int qpow(int base, int b, int ret = 1) {
        for (; b; b >>= 1, base = 1ll * base * base % mod)
            if (b & 1)
                ret = 1ll * ret * base % mod;
        return ret;
    }
    
    int combination(const int n, const int m) {
        if (n < m)
            return 0;
        int ret = 1;
        for (int i = 2; i <= m; ++i)
            ret = 1ll * ret * i % mod;
        ret = qpow(ret, mod - 2);
        for (int i = n - m + 1; i <= n; ++i)
            ret = 1ll * ret * i % mod;
        return ret;
    }
    
    int main() {
        //freopen("in", "r", stdin);
        freopen("mobitel.in", "r", stdin);
        freopen("mobitel.out", "w", stdout);
        R = in(), C = in(), n = in();
        for (int i = 1; i <= R; ++i)
            for (int j = 1; j <= C; ++j)
                mp[i][j] = in();
    
        for (int i = 1; i < n; i = (n - 1) / ((n - 1) / i) + 1) {
            num[++nn] = (n - 1) / i;
            id[(n - 1) / i] = nn;
        }
    
        int cur = 0;
        f[1][1][id[(n - 1) / mp[1][1]]] = 1;
        for (int i = 2; i <= R + C - 1; ++i, cur ^= 1) {
            memset(f[cur], 0, sizeof(f[cur]));
            for (int j = std::max(1, i - C + 1); j <= R && i + 1 - j; ++j) {
                for (int k = 1; k <= nn; ++k) {
                    add(f[cur][j][id[num[k] / mp[j][i + 1 - j]]], f[cur ^ 1][j][k]);
                    add(f[cur][j][id[num[k] / mp[j][i + 1 - j]]], f[cur ^ 1][j - 1][k]);
                }
            }
        }
        cur ^= 1;
        int res = 0;
        for (int i = 1; i <= nn; ++i)
            add(res, f[cur][R][i]);
        printf("%d
    ", (combination(R + C - 2, C - 1) - res + mod) % mod);
        return 0;
    }
    

    T3、Lottery

    题目链接

    定义两个序列对应位置上不同的值的个数不超过 (k),则可称为 (k) 相似。
    现在有一个长度为 (n (n leq 10 ^ 4)) 的序列 (a),它有 (n−l+1) 个长度为 (l (l leq 10 ^ 4)) 的子串(第 (i) 个子串为 ([i, i + l - 1]))。
    (q (q leq 100)) 组询问,第 (j) 组询问给出一个 (k_j (k_j leq l)),求每个子串与多少个其它的子串可称为 (k_j) 相似。
    (Memory Limits: 32MB)

    (Sol)

    知道 ([x, x + l - 1], [y, y + l - 1] (x e y)) 的相似度,可以 (O(2)) (逃) 得出 ([x + 1, x + l], [y + 1, y + l]) 的相似度。
    (f_{i, j}) 表示与第 (i) 个子串 (j) 相似的子串数量,空间复杂度 (O(n^2)) ,显然不行;
    注意到询问数不超过 (100),可以预处理,将询问排序,空间复杂度降为 (O(nq)),可以通过。

    时间复杂度 (O(l log_2 q + n ^ 2 + n q))

    (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 = 1e4 + 5;
    
    struct query {
        int id, k;
    } b[105];
    int n, L, q, a[N], id[N];
    int mp[N][105], res[N];
    
    inline bool cmp_id(const query &i, const query &j) {
        return i.id < j.id;
    }
    
    inline bool cmp_k(const query &i, const query &j) {
        return i.k < j.k;
    }
    
    void work() {
        std::sort(b + 1, b + 1 + q, cmp_k);
        for (int i = 1; i <= L; ++i)
            id[i] = std::lower_bound(b + 1, b + 1 + q, (query){0, i}, cmp_k) - b;
        for (int i = 1, now; i <= n - L; ++i) {
            now = 0;
            for (int j = 1; j <= L; ++j)
                now += (a[j] != a[j + i]);
            ++mp[1][id[now]];
            ++mp[1 + i][id[now]];
            for (int x = 2, y; x + i <= n - L + 1; ++x) {
                y = x + i;
                now -= (a[x - 1] != a[y - 1]);
                now += (a[x + L - 1] != a[y + L - 1]);
                ++mp[x][id[now]];
                ++mp[y][id[now]];
            }
        }
        for (int i = 1; i <= n - L + 1; ++i)
            for (int j = 1; j <= q; ++j)
                mp[i][j] += mp[i][j - 1];
        std::sort(b + 1, b + 1 + q, cmp_id);
        for (int i = 1; i <= q; ++i) {
            for (int j = 1; j <= n - L + 1; ++j)
                printf("%d ", mp[j][id[b[i].k]]);
            puts("");
        }
    }
    
    void input() {
        n = in(), L = in();
        for (int i = 1; i <= n; ++i)
            a[i] = in();
    
        q = in();
        for (int i = 1; i <= q; ++i)
            b[i] = (query){i, in()};
    }
    
    int main() {
        //freopen("in", "r", stdin);
        freopen("lottery.in", "r", stdin);
        freopen("lottery.out", "w", stdout);
        input();
        work();
        return 0;
    }
    
  • 相关阅读:
    .Net Core ----通过XUnit进行接口单元测试(带请求头及参数)并用output输出结果
    .Net Core---- 通过EPPlus批量导出
    .Net Core---- 自带Json返回日期带T格式 解决
    You need tcl 8.5 or newer in order to run the Redis test
    exec: "docker-proxy": executable file not found in $PATH
    docker 如何清理垃圾呢
    docker run 报错——WARNING: IPv4 forwarding is disabled. Networking will not work.
    go 依赖包管理工具gb安装报错
    keepalived实现nginx高可用
    php命令行查看扩展信息
  • 原文地址:https://www.cnblogs.com/15owzLy1-yiylcy/p/11385532.html
Copyright © 2020-2023  润新知