zoukankan      html  css  js  c++  java
  • 「2019纪中集训Day23」解题报告

    T1、矩阵乘法

    题目链接

    给定一个 (n imes n (n leq 500)) 的矩阵 ((a_{i,j} leq 10 ^ 9))(q (q leq 6 imes 10 ^ 5)) 组询问,每次询问一个子矩阵的第 (k) 大元素 (保证存在)。

    (Sol)

    整体二分主席树,注意常数因子带来的影响;
    全场只有我一个常数怪 (95) 分。

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

    (Source)

    //#pragma GCC optimize(2)
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    inline 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, Q = 60005;
    
    struct info {
        int id, x1, y1, x2, y2, k;
    } b[N * N + Q];
    int n, m, q, id[N * N + Q], tmp1[N * N + Q], tmp2[N * N + Q], pos[N * N], res[Q];
    int nn, mp[N * N];
    
    struct persistable_segment_tree {
        int sum[N * N * 11], c[N * N * 11][2], rt[N * N], tot;
        void clear() {
            tot = 0;
        }
        int new_node() {
            ++tot;
            sum[tot] = c[tot][0] = c[tot][1] = 0;
            return tot;
        }
        void modify(int pos, int tl, int tr, int pre, int &p) {
            p = new_node(), sum[p] = sum[pre];
            ++sum[p];
            if (tl == tr)
                return ;
            int mid = (tl + tr) >> 1;
            if (mid >= pos) {
                c[p][1] = c[pre][1];
                modify(pos, tl, mid, c[pre][0], c[p][0]);
            } else {
                c[p][0] = c[pre][0];
                modify(pos, mid + 1, tr, c[pre][1], c[p][1]);
            }
        }
        int query(int l, int r, int tl, int tr, int pre, int p) {
            if (l <= tl && tr <= r)
                return sum[p] - sum[pre];
            int mid = (tl + tr) >> 1;
            if (mid < l)
                return query(l, r, mid + 1, tr, c[pre][1], c[p][1]);
            if (mid >= r)
                return query(l, r, tl, mid, c[pre][0], c[p][0]);
            return query(l, r, tl, mid, c[pre][0], c[p][0]) +
                   query(l, r, mid + 1, tr, c[pre][1], c[p][1]);
        }
    } T;
    
    void binary_search(int l, int r, int s, int t) {
        if (l > r || s > t)
            return ;
        if (l == r) {
            for (int i = s; i <= t; ++i)
                if (b[id[i]].id)
                    res[b[id[i]].id] = mp[l];
            return ;
        }
        int mid = (l + r) >> 1, p1 = 0, p2 = 0, tot = 0;
        T.clear();
        for (int i = s; i <= t; ++i) {
            if (!b[id[i]].id) {
                if (b[id[i]].k <= mp[mid]) {
                    T.modify(b[id[i]].y1, 1, n, T.rt[tot], T.rt[tot + 1]);
                    tmp1[++p1] = id[i];
                    pos[++tot] = b[id[i]].x1;
                } else {
                    tmp2[++p2] = id[i];
                }
            } else {
                int x, y, w;
                x = std::lower_bound(pos + 1, pos + 1 + tot, b[id[i]].x1) - pos;
                y = std::upper_bound(pos + 1, pos + 1 + tot, b[id[i]].x2) - pos - 1;
                w = T.query(b[id[i]].y1, b[id[i]].y2, 1, n, T.rt[x - 1], T.rt[y]);
                if (w >= b[id[i]].k) {
                    tmp1[++p1] = id[i];
                } else {
                    b[id[i]].k -= w;
                    tmp2[++p2] = id[i];
                }
            }
        }
        for (int i = 1; i <= p1; ++i)
            id[s + i - 1] = tmp1[i];
        for (int i = 1; i <= p2; ++i)
            id[s + p1 + i - 1] = tmp2[i];
        binary_search(l, mid, s, s + p1 - 1);
        binary_search(mid + 1, r, s + p1, t);
    }
    
    int main() {
        //freopen("in", "r", stdin);
        n = in(), q = in();
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= n; ++j) {
                b[++m] = (info){0, i, j, 0, 0, in()};
                mp[++nn] = b[m].k;
            }
        for (int i = 1; i <= q; ++i)
            b[++m] = (info){i, in(), in(), in(), in(), in()};
        for (int i = 1; i <= m; ++i)
            id[i] = i;
        std::sort(mp + 1, mp + 1 + nn);
        nn = std::unique(mp + 1, mp + 1 + nn) - mp - 1;
        binary_search(0, nn, 1, m);
        for (int i = 1; i <= q; ++i)
            printf("%d
    ", res[i]);
        return 0;
    }
    

    T2、Tree

    题目链接

    给定一张 (n (n leq 100)) 个点 (m (m leq 2000)) 条边的无相连通图,有边权 (C_i (0 leq C_i leq 100)),求最小标准差生成树。

    (Sol)

    可以将答案看做一个关于平均数的函数:

    [f(x) = sqrt{ frac{sum (C_i - x) ^ 2}{n - 1} } ]

    枚举 (x),将边权设为 (C_i - x),求最小生成树,再按最小生成树的真实平均数更新答案。
    具体证明我不会,大概是对于每个 (x) 得到的真实平均值一定是与 (x) 相邻的。

    时间复杂度 (O()能跑多少跑多少())

    (Source)

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    typedef double db;
    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, M = 2005;
    const db eps = 1e-5;
    
    db res, now;
    int n, m;
    int fa[N];
    struct edge {
        int u, v, w;
        inline bool operator < (const edge &b) const {
            return (this->w - now) * (this->w - now) < (b.w - now) * (b.w - now);
        }
    } e[M];
    
    int get_fa(int u) {
        return fa[u] == u ? u : fa[u] = get_fa(fa[u]);
    }
    
    void calc() {
        res = 1 << 30;
        for (now = 100; now > 0; now -= 0.25) {
            std::sort(e + 1, e + 1 + m);
            int now_edge = 0, _sum = 0, sum = 0;
            for (int i = 1; i <= n; ++i)
                fa[i] = i;
            for (int i = 1; i <= m && now_edge < n - 1; ++i) {
                int fx = get_fa(e[i].u), fy = get_fa(e[i].v);
                if (fx == fy)
                    continue;
                fa[fx] = fy;
                ++now_edge;
                _sum += e[i].w * e[i].w, sum += e[i].w;
            }
            db tmp = sqrt(((db)(n - 1) * _sum - sum * sum) / (n - 1) / (n - 1));
            if (res - tmp > eps)
                res = tmp;
        }
    }
    
    int main() {
        //freopen("in", "r", stdin);
        n = in(), m = in();
        if (n == 1) {
            puts("0.0000");
            return 0;
        }
        for (int i = 1; i <= m; ++i)
            e[i] = (edge){in(), in(), in()};
    
        calc();
    
        printf("%.4lf
    ", res);
        return 0;
    }
    

    T3、Points and Segments

    题目链接

    数轴上给定 (n (n leq 10 ^ 5)) 条线段,每条线段为红色或蓝色,给出一种颜色方案,使得数轴上所有点被不同颜色覆盖的次数差绝对值不超过 (1)

    (Sol)

    可以把原数轴上的点看作线段,点被颜色覆盖可以看作线段被覆盖;
    题目中([l,r]) 线段就可以当作新图中 ([l,r + 1]) 中所有线段被覆盖。

    问题转化为所有线段被覆盖的绝对值不超过 (1)
    (l)(r + 1) 连无向边,(l)(r + 1) 即为染成蓝色,否则染成红色;
    这样如果该图有欧拉回路 (所有点都是偶点),则两色覆盖次数都相等;
    若有奇点,那么奇点个数一定是偶数 (一条线段有两个端点),则从左到右第 (2i)(2i + 1) 个奇点连边;
    该图一定有欧拉回路,所有被覆盖的次数都相等;
    因为新加的边之间一定不会相交,所以把他们删掉后每个点只会少覆盖一次。

    时间复杂度 (O(n))

    (Source)

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    inline 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 l, r;
    } a[N];
    struct edge {
        int next, to;
    } e[N << 2];
    int ecnt = 1, head[N << 1];
    int n, nn, mp[N << 1], res[N], deg[N << 1];
    bool vis[N << 1], used[N << 1];
    
    inline void jb(const int u, const int v) {
        e[++ecnt] = (edge){head[u], v}, head[u] = ecnt;
        e[++ecnt] = (edge){head[v], u}, head[v] = ecnt;
        ++deg[u], ++deg[v];
    }
    
    void prep() {
        for (int i = 1; i <= n; ++i) {
            a[i] = (node){in(), in()};
            mp[++nn] = a[i].l, mp[++nn] = a[i].r;
        }
        std::sort(mp + 1, mp + 1 + nn);
        nn = std::unique(mp + 1, mp + 1 + nn) - mp - 1;
        for (int i = 1; i <= n; ++i) {
            a[i].l = std::lower_bound(mp + 1, mp + 1 + nn, a[i].l) - mp;
            a[i].r = std::lower_bound(mp + 1, mp + 1 + nn, a[i].r) - mp;
            jb(a[i].l, a[i].r);
        }
        int last = 0;
        for (int i = 1; i <= nn; ++i)
            if (deg[i] & 1) {
                if (!last)
                    last  = i;
                else
                    jb(last, i), last = 0;
            }
    }
    
    void dfs(const int u) {
        used[u] = 1;
        for (int i = head[u]; i; i = e[i].next) {
            if (!vis[i >> 1]) {
                vis[i >> 1] = 1;
                dfs(e[i].to);
                if (u > e[i].to)
                    res[i >> 1] = 1;
                else
                    res[i >> 1] = 0;
            }
        }
    }
    
    void work() {
        for (int i = 1; i <= nn; ++i)
            if (!used[i])
                dfs(i);
    }
    
    int main() {
        //freopen("in", "r", stdin);
        n = in();
        prep();
        work();
        for (int i = 1; i <= n; ++i)
            printf("%d ", res[i]);
        puts("");
        return 0;
    }
    
  • 相关阅读:
    在windows系统下,配置vue项目一键启动文件
    CSS中设置元素的圆角矩形
    CSS中使用文本阴影与元素阴影
    如何使用CSS3中的结构伪类选择器和伪元素选择器
    CSS中的z-index属性如何使用
    深入学习CSS中如何使用定位
    css中如何使用border属性与display属性
    深入了解CSS中盒子模型
    CSS中如果实现元素浮动和清除浮动,看这篇文章就足够了
    CSS标准文档流
  • 原文地址:https://www.cnblogs.com/15owzLy1-yiylcy/p/11402365.html
Copyright © 2011-2022 走看看