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


    (flag):统一博客格式 (咕)

    T1、走格子

    一个 (n imes m (4 leq n,m leq 500)) 的矩阵,矩阵里有四种元素:墙 ("#")、空区域 (''.")、起点 ("C")、终点 ("F")。
    有传送枪操作:向上下左右四个方向中的任意方向开枪,会在第一个碰到的墙上出现传送门,只能同时存在两个传送门,若已经存在两个传送门,开枪会使先出现的传送门消失,该操作不消耗时间。
    从起点开始移动,每次可以移向相邻的格子,或者在与有传送门的墙相邻的格子通过传送门 (必须要存在两个传送门),移动到与另一个传送门相邻的格子,两种操作均不能移动到墙上且都消耗一个单位的时间。
    从起点到终点至少要消耗几个单位的时间。
    保证地图边缘是一圈墙壁。

    (Sol)

    传送枪的使用:打可以打到的四个墙壁中最近的一面和另外一面,走到最近的那一面通过传送门。
    最短路即可。

    时间复杂度 (O ig( nm log_2 ( nm ) ig))

    (Source)

    //#pragma GCC Optmize(2)
    //#pragma GCC Optmize(3, "Ofast", "inline")
    #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 = 505;
    
    char mp[N][N];
    
    struct link {
        int l, r, u, d;
    } nxt[N][N];
    
    int n, m, d[N][N];
    int dx[] = {1, 0, -1, 0};
    int dy[] = {0, 1, 0, -1};
    bool vis[N][N];
    typedef std::pair<int, int> pii;
    typedef std::pair<int, pii> pip;
    
    int dijkstra() {
        std::priority_queue<pip> q;
        int ex, ey;
        memset(d, 0x3f, sizeof(d));
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j) {
                if (mp[i][j] == 'C')
                    q.push(pip(0, pii(i, j))), d[i][j] = 0;
                if (mp[i][j] == 'F')
                    ex = i, ey = j;
            }
        while (!q.empty()) {
            int nx = q.top().second.first, ny = q.top().second.second, tx, ty, tmp; q.pop();
            if (vis[nx][ny])
                continue;
            vis[nx][ny] = 1;
            if (nx == ex && ny == ey)
                return d[nx][ny];
            for (int i = 0; i < 4; ++i) {
                tx = nx + dx[i], ty = ny + dy[i];
                if (mp[tx][ty] == '#' || d[nx][ny] + 1 >= d[tx][ty])
                    continue;
                d[tx][ty] = d[nx][ny] + 1;
                q.push(pip(-d[tx][ty], pii(tx, ty)));
            }
            tmp = ny - nxt[nx][ny].l;
            chk_min(tmp, nx - nxt[nx][ny].u);
            chk_min(tmp, nxt[nx][ny].r - ny);
            chk_min(tmp, nxt[nx][ny].d - nx);
            ++tmp;
    
            tx = nx, ty = nxt[nx][ny].l;
            if (ny - ty > tmp && d[tx][ty] > d[nx][ny] + tmp)
                d[tx][ty] = d[nx][ny] + tmp, q.push(pip(-d[tx][ty], pii(tx, ty)));
            tx = nx, ty = nxt[nx][ny].r;
            if (ty - ny > tmp && d[tx][ty] > d[nx][ny] + tmp)
                d[tx][ty] = d[nx][ny] + tmp, q.push(pip(-d[tx][ty], pii(tx, ty)));
            tx = nxt[nx][ny].u, ty = ny;
            if (nx - tx > tmp && d[tx][ty] > d[nx][ny] + tmp)
                d[tx][ty] = d[nx][ny] + tmp, q.push(pip(-d[tx][ty], pii(tx, ty)));
            tx = nxt[nx][ny].d, ty = ny;
            if (tx - nx > tmp && d[tx][ty] > d[nx][ny] + tmp)
                d[tx][ty] = d[nx][ny] + tmp, q.push(pip(-d[tx][ty], pii(tx, ty)));
        }
        return -1;
    }
    
    int main() {
        //freopen("in", "r", stdin);
        freopen("cell.in", "r", stdin);
        freopen("cell.out", "w", stdout);
        n = in(), m = in();
        memset(nxt, -1, sizeof(nxt));
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j)
                while (mp[i][j] != '.' && mp[i][j] != 'C' && mp[i][j] != 'F' && mp[i][j] != '#')
                    mp[i][j] = getchar();
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j) {
                if (mp[i][j] == '#')
                    continue;
                if (mp[i - 1][j] != '#')
                    nxt[i][j].u = nxt[i - 1][j].u;
                else
                    nxt[i][j].u = i;
                if (mp[i][j - 1] != '#')
                    nxt[i][j].l = nxt[i][j - 1].l;
                else
                    nxt[i][j].l = j;
            }
        for (int i = n; i; --i)
            for (int j = m; j; --j) {
                if (mp[i][j] == '#')
                    continue;
                if (mp[i + 1][j] != '#')
                    nxt[i][j].d = nxt[i + 1][j].d;
                else
                    nxt[i][j].d = i;
                if (mp[i][j + 1] != '#')
                    nxt[i][j].r = nxt[i][j + 1].r;
                else
                    nxt[i][j].r = j;
            }
        int res = dijkstra();
        if (!~res)
            puts("no");
        else
            printf("%d
    ", res);
        return 0;
    }
    

    T2、扭动的数

    有一棵以 (key (1 leq key leq 10 ^ {18})) 为键值、(val (1 leq val leq 10 ^ 6)) 为权值的 (BST),记每个节点的 (sum) 为以它为根的子树的 (val) 之和。给出 (n (1 leq n leq 300)) 个点的 (key)(val),需保证树上任意一条边上的两个点 (key) 不互质,求 (sum) 之和的最大值。若没有合法的树形态,输出 (-1)

    (Sol)

    考虑区间 (dp),现将序列按 (key) 排序,记 (f_{i,j,0/1}) 表示区间 ([i,j]) 可以作为 (i - 1(用0表示)/i + 1(用1表示)) 的子树时的最大 (sum) 和,暴力转移即可。
    (f_{i,j,0}) 为例 (不考虑边界):

    [f_{i,j,0}=max_{k = i + 1}^{j - 1}{f_{i,k - 1,1}+f_{k + 1,j,0}+sum[i,j]} , ig( ( key_{i - 1}, key_k ) > 1 ig) ]

    时间复杂度 (O(n ^ 3))

    (Source)

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    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 __) { _ = _ > __ ? _ : __; }
    
    const int N = 305;
    
    int n, sum[N], id[N];
    long long key[N], tmp[N], f[N][N][2];
    bool mp[N][N];
    
    long long gcd(long long _, long long __) {
        return __ ? gcd(__, _ % __) : _;
    }
    
    inline bool cmp(const int &i, const int j) {
        return key[i] < key[j];
    }
    
    void init() {
        for (int i = 1; i <= n; ++i)
            id[i] = i;
        std::sort(id + 1, id + 1 + n, cmp);
        for (int i = 1; i <= n; ++i)
            tmp[i] = key[id[i]];
        for (int i = 1; i <= n; ++i)
            key[i] = tmp[i];
        for (int i = 1; i <= n; ++i)
            tmp[i] = sum[id[i]];
        for (int i = 1; i <= n; ++i)
            sum[i] = sum[i - 1] + tmp[i];
        for (int i = 0; i < n; ++i)
            for (int j = i + 1; j <= n; ++j)
                mp[i][j] = gcd(key[i], key[j]) > 1;
    }
    
    int main() {
        //freopen("in", "r", stdin);
        freopen("tree.in", "r", stdin);
        freopen("tree.out", "w", stdout);
        n = in();
        for (int i = 1; i <= n; ++i)
            key[i] = lin(), sum[i] = in();
        init(); 
        for (int i = 1; i <= n; ++i) {
            if (mp[i - 1][i])
                f[i][i][0] = sum[i] - sum[i - 1];
            if (mp[i][i + 1])
                f[i][i][1] = sum[i] - sum[i - 1];
        }
        for (int len = 2; len <= n; ++len) {
            for (int i = 1; i + len - 1 <= n; ++i) {
                int j = i + len - 1;
                if (f[i + 1][j][0]) {
                    if (mp[i - 1][i])
                        chk_max(f[i][j][0], f[i + 1][j][0] + sum[j] - sum[i - 1]);
                    if (mp[i][j + 1])
                        chk_max(f[i][j][1], f[i + 1][j][0] + sum[j] - sum[i - 1]);
                }
                if (f[i][j - 1][1]) {
                    if (mp[i - 1][j])
                        chk_max(f[i][j][0], f[i][j - 1][1] + sum[j] - sum[i - 1]);
                    if (mp[j][j + 1])
                        chk_max(f[i][j][1], f[i][j - 1][1] + sum[j] - sum[i - 1]);
                }
                for (int k = i + 1; k < j; ++k) {
                    if (mp[i - 1][k] && f[i][k - 1][1] && f[k + 1][j][0])
                        chk_max(f[i][j][0], f[i][k - 1][1] + f[k + 1][j][0] + sum[j] - sum[i - 1]);
                    if (mp[k][j + 1] && f[i][k - 1][1] && f[k + 1][j][0])
                        chk_max(f[i][j][1], f[i][k - 1][1] + f[k + 1][j][0] + sum[j] - sum[i - 1]);
                }
            }
        }
        if (f[1][n][0])
            printf("%lld
    ", f[1][n][0]);
        else
            puts("-1");
        return 0;
    }
    

    T3、旋转子段

    给定一个长度为 (n) 的排列 ({a_i}),可以翻转一个子区间,求翻转一次后第 (i) 个位置上的数等于 (i) 的最多数量。

    (Sol)

    翻转区间的左右端点一定有一个会造成贡献,考虑枚举这些左右端点并快速计算翻转区间的贡献。
    注意到对于任意一个无序数对 ((i,a_i)),只对与它旋转中心相等且不比它短的区间有贡献,所以可以把所有旋转中心相等无序数对放在一起,按长短从小到大枚举即可满足需求。

    时间复杂度 (O(n))

    Source:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    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 = 1e5 + 5;
    
    int n, a[N], p[N], pre[N];
    
    typedef std::pair<int, int> pii;
    std::vector<pii> s[N << 1];
    
    int main() {
        //freopen("in", "r", stdin);
        freopen("rotate.in", "r", stdin);
        freopen("rotate.out", "w", stdout);
        n = in();
        for (int i = 1; i <= n; ++i)
            a[i] = in(), p[a[i]] = i, pre[i] = pre[i - 1] + (a[i] == i);
        for (int i = 1; i <= n; ++i) {
            if (a[i] <= i)
                s[i + a[i]].push_back(pii(i, a[i]));
            if (p[i] < i)
                s[i + p[i]].push_back(pii(i, p[i]));
        }
        int res = pre[n];
        for (int i = 1; i <= n + n; ++i)
            for (unsigned j = 0; j < s[i].size(); ++j)
                chk_max(res, (int)j + 1 + pre[s[i][j].second - 1] + pre[n] - pre[s[i][j].first]);
        printf("%d
    ", res);
        return 0;
    }
    
  • 相关阅读:
    解决Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.7.0:compile
    eclipse下解决明明有jar包,却找不到的问题
    Ngnix负载均衡安装及配置
    Redis入门教程(二)
    js监听组合按键
    js清空数组的方法
    js判断浏览器是否支持webGL
    Maven入门教程(一)
    杂记
    第一天 Requests库入门
  • 原文地址:https://www.cnblogs.com/15owzLy1-yiylcy/p/11329391.html
Copyright © 2020-2023  润新知