• 省选测试37


    A 小A的树

    题目大意 : 在一颗树中求所有点对的距离中最大的k个

    • 线段树维护直径可以在nlogn或nlogn2的时间内在一个区间内的数中找出离区间外一点距离最远的点

    • 于是可以把点对分成n类,第i类的是i与i之前的匹配,放到优先队列里找最远的点对,然后就可以把i点匹配的区间分成两半,然后再放进队列里

    Code

    Show Code
    #include <queue>
    #include <cstdio>
    #define ls (rt << 1)
    #define rs (rt << 1 | 1)
    
    using namespace std;
    typedef long long ll;
    const int N = 2e5 + 5;
    
    int read(int x = 0, int f = 1, char c = getchar()) {
        for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
        for (; c >='0' && c <='9'; c = getchar()) x = x * 10 + c - '0';
        return x * f;
    }
    
    struct Edge {
        int n, t, d;
    }e[N*2];
    int h[N], edc;
    
    void Add(int x, int y, int z) {
        e[++edc] = (Edge) {h[x], y, z}; h[x] = edc;
    }
    
    int n, k, dep[N], fa[N], son[N], sz[N], tp[N];
    ll d[N];
    
    void Dfs(int x) {
        dep[x] = dep[fa[x]] + 1; sz[x] = 1;
        for (int i = h[x], y; i; i = e[i].n) {
            if ((y = e[i].t) == fa[x]) continue;
            fa[y] = x; d[y] = d[x] + e[i].d;
            Dfs(y); sz[x] += sz[y];
            if (sz[son[x]] < sz[y]) son[x] = y;
        }
    }
    
    void Dfs(int x, int top) {
        tp[x] = top;
        if (son[x]) Dfs(son[x], top);
        for (int i = h[x], y; i; i = e[i].n)
            if ((y = e[i].t) != fa[x] && y != son[x]) Dfs(y, y);
    }
    
    ll Dis(int x, int y) {
        ll ans = d[x] + d[y];
        while (tp[x] != tp[y])
            dep[tp[x]] > dep[tp[y]] ? x = fa[tp[x]] : y = fa[tp[y]];
        return ans - 2 * d[dep[x] < dep[y] ? x : y];
    }
    
    struct Tree {
        int x, y; ll d;
    }t[N*4];
    
    Tree operator + (const Tree &a, const Tree &b) {
        Tree c = a.d > b.d ? a : b;
        int x[] = {a.x, a.y}, y[] = {b.x, b.y};
        for (int i = 0; i < 2; ++i) {
            for (int j = 0; j < 2; ++j) {
                ll d = Dis(x[i], y[j]);
                if (d > c.d) c = (Tree) {x[i], y[j], d};
            }
        }
        return c;
    }
    
    void Build(int rt, int l, int r) {
        if (l == r) return t[rt] = (Tree) {l, l}, void();
        int mid = l + r >> 1;
        Build(ls, l, mid); Build(rs, mid+1, r);
        t[rt] = t[ls] + t[rs];
    }
    
    Tree Ask(int rt, int l, int r, int x, int y) {
        if (x <= l && r <= y) return t[rt];
        int mid = l + r >> 1;
        if (y <= mid) return Ask(ls, l, mid, x, y);
        if (x >  mid) return Ask(rs, mid+1, r, x, y);
        return Ask(ls, l, mid, x, y) + Ask(rs, mid+1, r, x, y);
    }
    
    struct Node {
        int l, r, x, mid; ll d;
        Node() {}
        Node(int a, int b, int c) {
            l = a; r = b; x = c;
            Tree t = Ask(1, 1, n, l, r);
            ll d1 = Dis(x, t.x), d2 = Dis(x, t.y);
            if (d1 > d2) mid = t.x, d = d1;
            else mid = t.y, d = d2;
        }
    };
    
    bool operator < (const Node &a, const Node &b) {
        return a.d < b.d;
    }
    
    priority_queue<Node> q;
    
    int main() {
        freopen("tree.in", "r", stdin);
        freopen("tree.out", "w", stdout);
        n = read(); k = read();
        for (int i = 1; i < n; ++i) {
            int x = read(), y = read(), z = read();
            Add(x, y, z); Add(y, x, z);
        }
        Dfs(1); Dfs(1, 1); Build(1, 1, n);
        for (int i = 2; i <= n; ++i)
            q.push(Node(1, i-1, i));
        while (k--) {
            Node a = q.top(); q.pop();
            printf("%lld
    ", a.d);
            int l = a.l, r = a.r, mid = a.mid, x = a.x;
            if (l < mid) q.push(Node(l, mid-1, x));
            if (mid < r) q.push(Node(mid+1, r, x));
        }
        return 0;
    }
    

    B 小B的序列

    题目大意 : 区间取与,区间取或,区间查最大值

    • 线段树维护区间与和,或和,区间最大值,修改的时候,如果这个区间与和和或和改变了一样,就区间加变化值

    • 正确性好像挺显然,比如取与的时候,会把1变成0,如果这个区间里与和修改的地方和或和修改的地方一样,那么一定所有数都会改变一样的只

    • 时间复杂度的话不会证明,大概就是修改的时候会将区间变得更平?

    Code

    Show Code
    #include <cstdio>
    #include <algorithm>
    #define ls (rt << 1)
    #define rs (rt << 1 | 1)
    
    using namespace std;
    const int N = 2e5 + 5;
    
    int read(int x = 0, int f = 1, char c = getchar()) {
        for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
        for (; c >='0' && c <='9'; c = getchar()) x = x * 10 + c - '0';
        return x * f;
    }
    
    int n, m, a[N];
    
    struct Tree {
        int m, a, o, tag;
    }t[N*4];
    
    void Pushup(int rt) {
        t[rt].a = t[ls].a & t[rs].a;
        t[rt].o = t[ls].o | t[rs].o;
        t[rt].m = max(t[ls].m, t[rs].m);
    }
    
    void Update(int rt, int w) {
        t[rt].m += w; t[rt].a += w; t[rt].o += w; t[rt].tag += w;
    }
    
    void Pushdown(int rt) {
        if (!t[rt].tag) return;
        Update(ls, t[rt].tag);
        Update(rs, t[rt].tag);
        t[rt].tag = 0;
    }
    
    void Build(int rt, int l, int r) {
        if (l == r) return t[rt] = (Tree) {a[l], a[l], a[l]}, void();
        int mid = l + r >> 1;
        Build(ls, l, mid); Build(rs, mid+1, r);
        Pushup(rt);
    }
    
    void And(int rt, int l, int r, int x, int y, int w) {
        if (x <= l && r <= y && (w & t[rt].a) - t[rt].a == (w & t[rt].o) - t[rt].o) 
            return Update(rt, (w & t[rt].a) - t[rt].a);
        int mid = l + r >> 1; Pushdown(rt);
        if (x <= mid) And(ls, l, mid, x, y, w);
        if (y >  mid) And(rs, mid+1, r, x, y, w);
        Pushup(rt);
    }
    
    void Or(int rt, int l, int r, int x, int y, int w) {
        if (x <= l && r <= y && (w | t[rt].a) - t[rt].a == (w | t[rt].o) - t[rt].o) 
            return Update(rt, (w | t[rt].a) - t[rt].a);
        int mid = l + r >> 1; Pushdown(rt);
        if (x <= mid) Or(ls, l, mid, x, y, w);
        if (y >  mid) Or(rs, mid+1, r, x, y, w);
        Pushup(rt);
    }
    
    int Ask(int rt, int l, int r, int x, int y) {
        if (x <= l && r <= y) return t[rt].m;
        int mid = l + r >> 1, ans = 0; Pushdown(rt);
        if (x <= mid) ans = Ask(ls, l, mid, x, y);
        if (y >  mid) ans = max(ans, Ask(rs, mid+1, r, x, y));
        return ans;
    }
    
    int main() {
        freopen("sequence.in", "r", stdin);
        freopen("sequence.out", "w", stdout);
        n = read(); m = read();
        for (int i = 1; i <= n; ++i)
            a[i] = read();
        Build(1, 1, n);
        while (m--) {
            int od = read(), l = read(), r = read();
            if (od == 1) And(1, 1, n, l, r, read());
            else if (od == 2) Or(1, 1, n, l, r, read());
            else printf("%d
    ", Ask(1, 1, n, l, r));
        }
        return 0;
    }
    

    C 小C的利是 (Unaccepted)

    题目大意 : 在n×n的阵中选n个不为-1的数,每行每列最多选1个数,问多少种选法可以使得选的数的和与k同余

    • 正解是行列式,但数据挺水,搜索到一定次数输出No就能过

    Code

    Show Code
  • 相关阅读:
    【NOIP模拟】排序
    【NOIP模拟】企鹅矩阵
    【NOIP模拟】花花森林
    【BZOJ1045】糖果传递
    【BZOJ1271】秦腾的教学评估
    【POJ3714】Raid
    【TYVJ1424】占卜DIY
    【POJ3190】The Pilots Brothers' refrigerator
    【HDU1055】Color a Tree
    【NOIP2012】国王游戏
  • 原文地址:https://www.cnblogs.com/shawk/p/14559077.html
Copyright © 2020-2023  润新知