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


    T1、迷宫

    一个 $ n imes m (n leq 5, m leq 10 ^ 5) $ 的矩阵,(0) 表示格子不能走,(1) 表示格子可以走,只能向上、下、右三个方向走。有 $ q (q leq 5 imes 10 ^ 4) $ 次操作,操作有两种:
    1、修改某个格子的类型;
    2、查询从 $ (a, b) $ 到 ((c, d)) 的最短路,不可达输出 (-1)。 (保证后者在前者右边)。

    (Sol)

    观察到 (n leq 5),这是很重要的提示。
    在左右方向上建线段树,线段树每个叶子节点为一个矩阵,该矩阵描述了这一列上点的连通关系,特别地,若该列某一行的格子不能走,它到自己在矩阵中应表示为不可到达;
    合并时枚举中间点 (n ^ 3) 暴力合并。
    然而我考场上写了常数较大的做法:每个叶子节点的矩阵描述这一列到下一列的连通关系,合并同理;这样写修改时要改两个点,然后就没了。

    (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;
    }
    
    const int N = 2e5 + 5;
    
    int n, m, q, mp[7][N];
    
    template<typename T>inline void chk_min(T &_, T __) {
        _ = !~_ ? __ : (_ < __ ? _ : __);
    }
    struct node {
        int a[6][6];
        node () {
            memset(a, -1, sizeof(a));
        }
        inline int* operator [] (const int x) {
            return a[x];
        }
        inline node operator + (node b) const {
            node ret;
            for (int k = 1; k <= n; ++k)
                for (int i = 1; i <= n; ++i)
                    if (~a[i][k])
                        for (int j = 1; j <= n; ++j)
                            if (~b[k][j])
                                chk_min(ret[i][j], a[i][k] + b[k][j] + 1);
            return ret;
        }
    } ;
    struct segment_tree {
        node t[N << 2];
        void init(int x, int p) {
            for (int i = 1, j; i <= n; ++i) {
                for (j = i; j <= n && mp[j][x]; ++j)
                    t[p][i][j] = j - i;
                for (; j <= n; ++j)
                    t[p][i][j] = -1;
                for (j = i; j && mp[j][x]; --j)
                    t[p][i][j] = i - j;
                for (; j; --j)
                    t[p][i][j] = -1;
            }
        }
        inline void push_up(int p) {
            t[p] = t[p << 1] + t[p << 1 | 1];
        }
        void build(int tl, int tr, int p) {
            if (tl == tr)
                return init(tl, p);
            int mid = (tl + tr) >> 1;
            build(tl, mid, p << 1), build(mid + 1, tr, p << 1 | 1);
            push_up(p);
        }
        void modify(int pos, int tl, int tr, int p) {
            if (tl == tr)
                return init(tl, p);
            int mid = (tl + tr) >> 1;
            if (mid >= pos)
                modify(pos, tl, mid, p << 1);
            else
                modify(pos, mid + 1, tr, p << 1 | 1);
            push_up(p);
        }
        node query(int l, int r, int tl, int tr, int p) {
            if (l <= tl && tr <= r)
                return t[p];
            int mid = (tl + tr) >> 1;
            if (mid < l)
                return query(l, r, mid + 1, tr, p << 1 | 1);
            if (mid >= r)
                return query(l, r, tl, mid, p << 1);
            return query(l, r, tl, mid, p << 1) +
                   query(l, r, mid + 1, tr, p << 1 | 1);
        }
    } T;
    
    int main() {
        //freopen("in", "r", stdin);
        freopen("maze.in", "r", stdin);
        freopen("maze.out", "w", stdout);
        n = in(), m = in(), q = in();
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j)
                mp[i][j] = in();
        T.build(1, m, 1);
    
        int a, b, c, d;
        node res;
        while (q--) {
            if (in() == 1) {
                a = in(), b = in();
                mp[a][b] ^= 1;
                T.modify(b, 1, m, 1);
            } else {
                a = in(), b = in(), c = in(), d = in();
                res = T.query(b, d, 1, m, 1);
                printf("%d
    ", res[a][c]);
            }
        }
        return 0;
    }
    

    T2、猛汉王

    平面上有 (n (n leq 10 ^ 5)) 个黑点,(m (n leq 10 ^ 5)) 个白点,若黑点 (x) 与白点 (y) 的曼哈顿距离小于等于 (d (d leq 10 ^ 9)),则 (x)(y) 连边,否则 (y)(x) 连边。
    同色点之间的连边方向不确定,求至少有一个黑点和白点的三元环的最小值和最大值。

    (Sol)

    显然地,三元环上有两个同色点。
    由于我们不关心异色边是怎么连的,只关心是否能为同向环上的边,所以黑色点和白色点同理。
    (cover(x)) 表示 (x) 能覆盖的白色点,(cover ( x, y )) 表示 (x, y) 共同覆盖到的白色点。
    以下以最大值为例 (最小值同理)。

    [egin{eqnarray} Ans &=& sum_{x, y in S} max {cover(x) - cover{(x,y)}, cover(y) - cover(x, y) } otag \ &=& sum_{x, y in S} max { cover(x), cover(y) } - sum_{x, y in S} cover(x, y) otag \ &=& sum_{x, y in S} max { cover(x), cover(y) } - sum_{x in S} inom{cover(x)}{2} otag \ end{eqnarray} ]

    只要求出 (cover(x)) 即可;
    扫描线维护以下就行了。

    (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 = 1e5 + 5;
    struct node {
        int x, y;
    } a[N], b[N];
    struct options {
        long long x; int y, z, id;
        int typ;
    } opt[N * 3];
    
    int n, m, d, nn, cntx[N], cnty[N];
    long long ans_min, ans_max;
    
    long long tmp[N * 3];
    inline bool cmpx(const options &i, const options &j) {
        if (i.x == j.x)
            return i.typ == 0 ? 1 : j.typ != 0;
        return i.x < j.x;
    }
    void prep() {
        nn = 0;
        for (int i = 1; i <= m; ++i)
            tmp[i] = b[i].y;
        for (int i = 1; i <= n; ++i)
            tmp[i + m] = 1ll * a[i].y - d;
        for (int i = 1; i <= n; ++i)
            tmp[i + n + m] = 1ll * a[i].y + d;
        std::sort(tmp + 1, tmp + 1 + n + n + m);
        nn = std::unique(tmp + 1, tmp + 1 + n + n + m) - tmp - 1;
        for (int i = 1; i <= m; ++i) {
            opt[i].typ = opt[i].id = 0;
            opt[i].x = b[i].x;
            opt[i].y = std::lower_bound(tmp + 1, tmp + 1 + nn, b[i].y) - tmp;
            opt[i].z = 0;
        }
        for (int i = 1; i <= n; ++i) {
            opt[i + m].typ = -1;
            opt[i + m].id = i;
            opt[i + m].x = 1ll * a[i].x - d - 1;
    
            opt[i + n + m].typ = 1;
            opt[i + n + m].id = i;
            opt[i + n + m].x = 1ll * a[i].x + d;
            
            opt[i + m].y = opt[i + n + m].y = std::lower_bound(tmp + 1, tmp + 1 + nn, 1ll * a[i].y - d) - tmp;
            opt[i + m].z = opt[i + n + m].z = std::lower_bound(tmp + 1, tmp + 1 + nn, 1ll * a[i].y + d) - tmp;
        }
        std::sort(opt + 1, opt + 1 + n + n + m, cmpx);
    }
    
    struct binary_index_tree {
        int a[N * 3];
        inline void init() { memset(a, 0, sizeof(a)); }
        void insert(int p, int k) { for (; p <= nn; p += (p & -p)) a[p] += k; }
        int ask(int p, int ret = 0) { for (; p; p -= (p & -p)) ret += a[p]; return ret; }
    } bit;
    
    void work() {
        prep();
        bit.init();
        for (int i = 1; i <= n + n + m; ++i) {
            if (!opt[i].typ) {
                bit.insert(opt[i].y, 1);
            } else {
                cntx[opt[i].id] += opt[i].typ * (bit.ask(opt[i].z) - bit.ask(opt[i].y - 1));
            }
        }
    }
    
    int main() {
        //freopen("in", "r", stdin);
        freopen("mhw.in", "r", stdin);
        freopen("mhw.out", "w", stdout);
        n = in(), m = in(), d = in();
        for (int i = 1; i <= n; ++i) {
            a[i] = (node){in(), in()};
            a[i] = (node){a[i].x + a[i].y, a[i].x - a[i].y};
        }
        for (int i = 1; i <= m; ++i) {
            b[i] = (node){in(), in()};
            b[i] = (node){b[i].x + b[i].y, b[i].x - b[i].y};
        }
        work();
        std::swap(a, b);
        std::swap(n, m);
        std::swap(cntx, cnty);
        work();
    
        std::sort(cntx + 1, cntx + 1 + n);
        std::sort(cnty + 1, cnty + 1 + m);
        for (int i = 1; i <= n; ++i) {
            ans_min += 1ll * cntx[i] * (n - i);
            ans_min -= 1ll * cntx[i] * (cntx[i] - 1) / 2;
            ans_max += 1ll * cntx[i] * (i - 1);
            ans_max -= 1ll * cntx[i] * (cntx[i] - 1) / 2;
        }
        for (int i = 1; i <= m; ++i) {
            ans_min += 1ll * cnty[i] * (m - i);
            ans_min -= 1ll * cnty[i] * (cnty[i] - 1) / 2;
            ans_max += 1ll * cnty[i] * (i - 1);
            ans_max -= 1ll * cnty[i] * (cnty[i] - 1) / 2;
        }
        printf("%lld %lld
    ", ans_min, ans_max);
        return 0;
    }
    

    T3、工厂

    给定一个二分图,可以加边,使得二分图的所有极大匹配都是完美匹配。

    (Sol)

    先证明一件事:当且仅当这个二分图里的每个联通块左右点数相同且边都是连满的 ((A)),满足题意 ((B))
    (B)(A) 是显然的,不再赘述;
    (A)(B) 可以用反证法:
    若联通块中有两个异侧点 (a)(b) 之间没有连边,找到一条从 (a)(b) 的路径,把奇数边加入匹配边集,在此基础上找到一个极大匹配,找到的应该是完美匹配;将路径上奇数边取出匹配边集,偶数边加入,此时也是一个极大匹配但不是完美匹配。
    (QED)

    将几个联通块合并的代价为 (( sum x_i ) ^ 2)
    (f_{s,i}) 表示当前选出联通块集合为 (s),已经安排了点数和为 (2i) 的联通块 (使它们满足上述条件) 的最小代价。
    转移有两种:

    [egin{eqnarray} f_{s igcup { x },i} &=& min(f_{s igcup { x }, i}, f_{S,i}) otag \ f_{s, i} &=& min_{j = 0} ^ {i - 1} { f_{s,j} + (i - j) ^ 2 } , (Sigma_{x in s} = Sigma_{y in s}) otag end{eqnarray} ]

    由于状态数太多,这样也过不了,可以优化状压的方式;
    我们只关心每一种联通块 (以左右点数分类) 有几个,可以用变进制数来优化状态数;
    出题人告诉我们最多有 (172032) 种状态。

    (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 = 35, inf = 0x3f3f3f3f;
    
    int n, m, mp[N << 1][N << 1], tmp_x, tmp_y, init_edge, buc[N][N], pre[N << 1], f[172033][N], num[N << 1];
    bool vis[N << 1];
    
    struct node {
        int x, y;
        inline bool operator < (const node &b) const {
            return buc[this->x][this->y] < buc[b.x][b.y];
        }
        inline bool operator == (const node &b) const {
            return this->x == b.x && this->y == b.y;
        }
    } a[N + N];
    
    void prep(const int u) {
        vis[u] = 1;
        if (u <= n)
            ++tmp_x;
        else
            ++tmp_y;
        for (int i = 1; i <= mp[u][0]; ++i)
            if (!vis[mp[u][i]])
                prep(mp[u][i]);
    }
    
    int main() {
        //freopen("in", "r", stdin);
        freopen("factory.in", "r", stdin);
        freopen("factory.out", "w", stdout);
        n = in();
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= n; ++j) {
                char c = getchar();
                while (c < '0' || c > '1')
                    c = getchar();
                if (c == '1') {
                    mp[i][++mp[i][0]] = j + n;
                    mp[j + n][++mp[j + n][0]] = i;
                }
                init_edge += c == '1';
            }
        for (int i = 1; i <= n + n; ++i)
            if (!vis[i]) {
                tmp_x = tmp_y = 0;
                prep(i);
                ++buc[tmp_x][tmp_y];
            }
    
        for (int i = 0; i <= n; ++i)
            for (int j = 0; j <= n; ++j)
                if (buc[i][j])
                    a[++m] = (node){i, j};
        std::sort(a + 1, a + 1 + m);
        m = std::unique(a + 1, a + 1 + m) - a - 1;
        pre[1] = 1;
        for (int i = 2; i <= m + 1; ++i)
            pre[i] = pre[i - 1] * (buc[a[i - 1].x][a[i - 1].y] + 1);
    
        memset(f, inf, sizeof(f));
        f[0][0] = 0;
    
        int nowx, nowy;
        for (int s = 0; s < (pre[m + 1]); ++s) {
            nowx = nowy = 0;
            for (int i = 1; i <= m; ++i)
                num[i] = s % pre[i + 1] / pre[i];
            for (int i = 1; i <= m; ++i)
                nowx += num[i] * a[i].x, nowy += num[i] * a[i].y;
            if (nowx == nowy)
                for (int i = 0; i < nowx; ++i)
                    chk_min(f[s][nowx], f[s][i] + (nowx - i) * (nowx - i));
            for (int i = 1; i <= m; ++i)
                if (num[i] < buc[a[i].x][a[i].y])
                    for (int j = 0; j <= n; ++j)
                        chk_min(f[s + pre[i]][j], f[s][j]);
        }
        printf("%d
    ", f[pre[m + 1] - 1][n] - init_edge);
    
        return 0;
    }
    
    
    
  • 相关阅读:
    2017浙江工业大学-校赛决赛 猜猜谁是我
    2017浙江工业大学-校赛决赛 竹之书
    2017浙江工业大学-校赛决赛 小M和天平
    2017"百度之星"程序设计大赛
    2017"百度之星"程序设计大赛
    2017"百度之星"程序设计大赛
    2017"百度之星"程序设计大赛
    2015-2016机器人操作系统(ROS)及其应用暑期学校资料汇总 ROS Summer School 持续更新
    2016“智能无人系统”暑期学校总结
    ROS_Kinetic_20 ROS基础补充
  • 原文地址:https://www.cnblogs.com/15owzLy1-yiylcy/p/11348880.html
Copyright © 2020-2023  润新知