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

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

T1、走格子

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

\(Sol\)

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

时间复杂度 \(O \big( nm \ log_2 ( nm ) \big)\)

\(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\n", 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]\} \ , \big( ( key_{i - 1}, key_k ) > 1 \big) \]

时间复杂度 \(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\n", 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\n", res);
    return 0;
}
posted @ 2019-08-09 20:14  15owzLy1  阅读(202)  评论(0编辑  收藏  举报