标签: LCT

  • LOJ121 「离线可过」动态图连通性

    做法很简单,然而写起来一堆坑..

    #include <assert.h>
    #include <algorithm>
    #include <cstdio>
    #include <iostream>
    #include <vector>
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    const int_t LARGE = 1e6;
    const int_t INF = 0x7fffffff;
    // const int_t
    struct State {
        int_t val;
        int_t id;
        State operator+(const State& state) const {
            if (val < state.val) return *this;
            return state;
        }
    };
    struct Query {
        int_t v1, v2, id;
    };
    struct Edge {
        int_t from, to, id;
    };
    struct Node {
        int_t id = -2;
        Node* chds[2] = {nullptr, nullptr};
        Node*& left;
        Node*& right;
        Node* parent = nullptr;
        State state, val;
        Edge edge;
        bool flipped = false;
        Node(int_t id) : left(chds[0]), right(chds[1]) {
            this->state.id = id;
            this->state.val = INF;
            this->val = state;
        }
        void updateFrom(Node* node) {
            node->parent = this;
            val = val + node->val;
        }
        void maintain() {
            val = state;
            if (left) updateFrom(left);
            if (right) updateFrom(right);
        }
        int_t chdOf() const {
            if (parent == nullptr) return -1;
            if (this == parent->left) return 0;
            if (this == parent->right) return 1;
            return -1;
        }
        void flip() {
            flipped ^= 1;
            std::swap(left, right);
        }
        void pushDown() {
            if (flipped) {
                flipped = false;
                if (left) left->flip();
                if (right) right->flip();
            }
        }
    };
    void rotate(Node* node) {
        Node* Pr = node->parent;
        Pr->pushDown();
        node->pushDown();
        int_t side = node->chdOf();
        int_t sidep = Pr->chdOf();
        Node* Gr = Pr->parent;
        Pr->chds[side] = node->chds[side ^ 1];
        node->chds[side ^ 1] = Pr;
        node->parent = Gr;
        Pr->maintain();
        node->maintain();
        if (Gr && sidep != -1) {
            Gr->chds[sidep] = node;
            Gr->maintain();
        }
    }
    bool isAuxRoot(Node* node) {
        if (node == nullptr || node->parent == nullptr) return true;
        return node->chdOf() == -1;
    }
    void splay(Node* node) {
        assert(node);
        static Node* stack[LARGE + 1];
        int_t top = 0;
        Node* curr = node;
        while (true) {
            stack[top++] = curr;
            if (isAuxRoot(curr)) break;
            curr = curr->parent;
        }
        while (top) stack[--top]->pushDown();
        while (isAuxRoot(node) == false) {
            if (isAuxRoot(node->parent))
                rotate(node);
            else {
                if (node->chdOf() == node->parent->chdOf()) {
                    rotate(node->parent);
                } else {
                    rotate(node);
                }
                rotate(node);
            }
        }
    }
    void access(Node* node) {
        Node* last = nullptr;
        while (node) {
            splay(node);
            node->right = last;
            last = node;
            node->maintain();
            node = node->parent;
        }
    }
    Node* getRoot(Node* node) {
        access(node);
        splay(node);
        while (true) {
            node->pushDown();
            if (node->left == nullptr) break;
            node = node->left;
        }
        return node;
    }
    void changeRoot(Node* node) {
        access(node);
        splay(node);
        node->flip();
    }
    void link(Node* n1, Node* n2) {
        changeRoot(n2);
        access(n1);
        splay(n1);
        n1->pushDown();
        n1->right = n2;
        n1->maintain();
    }
    void cut(Node* n1, Node* n2) {
        changeRoot(n1);
        access(n2);
        splay(n1);
        n1->right = nullptr;
        n2->parent = nullptr;
        n1->maintain();
    }
    State query(Node* n1, Node* n2) {
        changeRoot(n1);
        access(n2);
        splay(n2);
        return n2->val;
    }
    int_t mat[5001][5001];
    int n, m;
    std::vector<Edge> edges;
    std::vector<Query> querys;
    
    int main() {
        // freopen("graph4.in", "r", stdin);
        static Node* nodes[LARGE + 1];
        scanf("%d%d", &n, &m);
        for (int_t i = 1; i <= n; i++) {
            nodes[i] = new Node(-1);
            nodes[i]->id = i;
        }
        for (int_t i = 1; i <= m; i++) {
            int opt, v1, v2;
            scanf("%d%d%d", &opt, &v1, &v2);
            if (v1 > v2) std::swap(v1, v2);
            if (opt == 0) {
                edges.push_back(Edge{v1, v2});
                edges.back().id = i;
                mat[v1][v2] = edges.back().id;
                nodes[n + edges.back().id] = new Node(edges.back().id);
                nodes[n + edges.back().id]->id = n + i;
                nodes[n + edges.back().id]->edge = edges.back();
            } else if (opt == 1) {
                nodes[n + mat[v1][v2]]->state.val = i;
                nodes[n + mat[v1][v2]]->maintain();
            } else {
                querys.push_back(Query{v1, v2, i});
            }
        }
        const auto pushEdge = [&](const Edge& edge) {
            if (getRoot(nodes[edge.from]) == getRoot(nodes[edge.to])) {
                State state = query(nodes[edge.from], nodes[edge.to]);
                //点的出现时间也是INF,避免把普通的点当成代表边的点
                if (state.val >= nodes[n + edge.id]->val.val) return;
                Node* oldEdge = nodes[n + state.id];
                // cut错边了!!
                cut(nodes[oldEdge->edge.from], oldEdge);
                cut(nodes[oldEdge->edge.to], oldEdge);
            }
            Node* currEdge = nodes[n + edge.id];
            link(nodes[edge.from], currEdge);
            link(nodes[edge.to], currEdge);
        };
        int_t pos = 0;
        for (const auto& query : querys) {
            while (pos < edges.size() && edges[pos].id < query.id) {
                pushEdge(edges[pos]);
                pos++;
            }
            Node *n1 = nodes[query.v1], *n2 = nodes[query.v2];
            if (getRoot(n1) != getRoot(n2)) {
                printf("N\n");
            } else if (::query(n1, n2).val < query.id) {
                printf("N\n");
            } else {
                printf("Y\n");
            }
        }
        return 0;
    }
  • SDOI2017 树点涂色

    假设我们可以通过某种东西维护出$f(n)$,表示n号点的树根的路径上所经过的不同颜色的点的个数(初始时$f(n)=depth(n)$),那么询问2,3显然就可以直接回答了。

    怎么维护呢?

    注意到每次操作是给树根到一个点的路径上染上不同的权值.

    这恰好和LCT的access操作很像。

    所以我们用若干棵辅助树来维护若干个若干条链。

    每个辅助树维护一条同色链。

    一次染色操作就相当于一次access操作,即把一条到根的链染成同一种颜色。

    然后考虑如何维护$f(n)$?

    考虑把一个点vtx染成新的颜色会发生什么。

    vtx的一条边从虚边变成了实边:

    这时代表vtx和这条边的另一个端点的颜色一定一样,另一个端点为根的整棵树答案减1.

    因为颜色数少了一种。

    反之,如果一条边从实边变成了虚边。

    这条边的另一个端点所有答案加1,因为这条变得两个端点原来颜色是相同的,但是现在不同了。

    注意别搞混DFS序。

    #include <cmath>
    #include <cstdio>
    #include <iostream>
    #include <string>
    #include <vector>
    using int_t = long long int;
    
    using std::cin;
    using std::cout;
    using std::endl;
    const int_t LARGE = 1e5;
    const int_t INF = 0x7fffffff;
    int_t DFSN[LARGE + 1], size[LARGE + 1], byDFSN[LARGE + 1], parent[LARGE + 1],
        first[LARGE + 1], depth[LARGE + 1];
    std::vector<int_t> graph[LARGE + 1];
    struct SegNode {
        int_t begin, end;
        SegNode *left, *right;
        int_t sum;
        int_t max;
        int_t mark;
        SegNode(int_t begin, int_t end) {
            this->left = this->right = nullptr;
            mark = sum = 0;
            max = -INF;
            this->begin = begin;
            this->end = end;
        }
        void add(int_t x) {
            sum += (end - begin + 1) * x;
            max += x;
            mark += x;
        }
        void pushDown() {
            if (begin != end) {
                if (mark) {
                    left->add(mark);
                    right->add(mark);
                    mark = 0;
                }
            }
        }
        void maintain() {
            if (begin != end) {
                sum = left->sum + right->sum;
                max = std::max(left->max, right->max);
            }
        }
        int_t querySum(int_t begin, int_t end) {
            if (end < this->begin || begin > this->end) return 0;
            if (this->begin >= begin && this->end <= end) {
                return this->sum;
            }
            this->pushDown();
            return left->querySum(begin, end) + right->querySum(begin, end);
        }
        int_t queryMax(int_t begin, int_t end) {
            if (end < this->begin || begin > this->end) return -INF;
            if (this->begin >= begin && this->end <= end) return this->max;
            pushDown();
            return std::max(left->queryMax(begin, end),
                            right->queryMax(begin, end));
        }
        void add(int_t begin, int_t end, int_t val) {
            if (end < this->begin || begin > this->end) return;
            if (this->begin >= begin && this->end <= end) {
                this->add(val);
                return;
            }
            pushDown();
            left->add(begin, end, val);
            right->add(begin, end, val);
            maintain();
        }
        template <class Func>
        static SegNode* build(int_t begin, int_t end, Func f) {
            SegNode* node = new SegNode(begin, end);
            if (begin != end) {
                int_t mid = (begin + end) / 2;
                node->left = build(begin, mid, f);
                node->right = build(mid + 1, end, f);
                node->maintain();
            } else {
                node->max = node->sum = f(begin);
            }
    
    #ifdef DEBUG
            cout << "interval " << begin << " " << end << " sum = " << node->sum
                 << " max = " << node->max << endl;
    #endif
            return node;
        }
    };
    SegNode* root = nullptr;
    struct Node {
        Node* chds[2] = {nullptr, nullptr};
        Node*& left;
        Node*& right;
        Node* parent = nullptr;
        int_t id;
        int_t minid;
        Node(int_t id) : left(chds[0]), right(chds[1]) {
            this->id = this->minid = id;
        }
        int_t chdOf() {
            if (parent == nullptr)
                return -1;
            else if (this == parent->left)
                return 0;
            else if (this == parent->right)
                return 1;
            return -1;
        }
    
        void maintain() {
            minid = id;
            if (left) {
                left->parent = this;
                minid = left->minid;
            }
            if (right) right->parent = this;
        }
        bool isAuxRoot() { return chdOf() == -1; }
        // bool isRoot() { return parent == nullptr; }
    };
    Node* nodes[LARGE + 1];
    void rotate(Node* node) {
        Node* Pr = node->parent;
        Node* Gr = Pr->parent;
        int_t side = node->chdOf();
        int_t sidep = Pr->chdOf();
        Pr->chds[side] = node->chds[side ^ 1];
        node->chds[side ^ 1] = Pr;
        node->parent = Gr;
        Pr->maintain();
        node->maintain();
        if (Gr && sidep != -1) {
            Gr->chds[sidep] = node;
            Gr->maintain();
        }
    }
    void splay(Node* node) {
        while (!(node->isAuxRoot())) {
            if (node->parent->isAuxRoot()) {
                rotate(node);
            } else {
                if (node->chdOf() == node->parent->chdOf()) {
                    rotate(node->parent);
                } else {
                    rotate(node);
                }
                rotate(node);
            }
        }
    }
    
    void access(Node* node) {
        Node* last = nullptr;
        while (node) {
            splay(node);
            //断掉原有链接
            if (node->right) {
                int_t vtx = node->right->minid;
                //重边->轻边 答案+1
                root->add(DFSN[vtx], DFSN[vtx] + size[vtx] - 1, 1);
            }
            node->right = last;
            node->maintain();
            //轻边->重边 答案-1
            if (node->right) {
                int_t vtx = node->right->minid;
                root->add(DFSN[vtx], DFSN[vtx] + size[vtx] - 1, -1);
            }
            last = node;
            node = node->parent;
        }
    }
    struct Data {
        int_t depth;
        int_t vtx;
        bool operator<(const Data& data) const { return depth < data.depth; }
    } seq[20][LARGE * 2 + 2];
    int_t count = 0;
    
    int_t n, m;
    void DFS(int_t vtx, int_t from = -1, int_t depth = 1) {
        parent[vtx] = from;
        size[vtx] = 1;
        seq[0][++count] = Data{depth, vtx};
        if (first[vtx] == 0) first[vtx] = count;
        DFSN[vtx] = ++DFSN[0];
        byDFSN[DFSN[0]] = vtx;
        ::depth[vtx] = depth;
        nodes[vtx] = new Node(vtx);
        for (int_t to : graph[vtx]) {
            if (to == from) continue;
            DFS(to, vtx, depth + 1);
            size[vtx] += size[to];
            seq[0][++count] = Data{depth, vtx};
            nodes[to]->parent = nodes[vtx];
        }
    }
    int_t getLCA(int_t v1, int_t v2) {
        v1 = first[v1];
        v2 = first[v2];
        if (v1 > v2) std::swap(v1, v2);
        int_t len = log2(v2 - v1 + 1);
        return std::min(seq[len][v1], seq[len][v2 - (1 << len) + 1]).vtx;
    }
    
    int main() {
        scanf("%lld%lld", &n, &m);
        for (int_t i = 1; i <= n - 1; i++) {
            int from, to;
            scanf("%d%d", &from, &to);
            graph[from].push_back(to);
            graph[to].push_back(from);
        }
        DFS(1);
    #ifdef DEBUG
        for (int_t i = 1; i <= n; i++) {
            cout << "by dfsn " << i << " = " << byDFSN[i] << endl;
        }
    #endif
        for (int_t i = 1; (1 << i) <= count; i++) {
            for (int_t j = 1; j + (1 << i) - 1 <= count; j++) {
                seq[i][j] = std::min(seq[i - 1][j], seq[i - 1][j + (1 << (i - 1))]);
            }
        }
        root =
            SegNode::build(1, n, [](int_t x) -> int_t { return depth[byDFSN[x]]; });
        for (int_t i = 1; i <= m; i++) {
            int opt, arg1, arg2;
            scanf("%d%d", &opt, &arg1);
            if (opt == 1) {
                access(nodes[arg1]);
            } else if (opt == 2) {
                scanf("%d", &arg2);
                int_t lca = DFSN[getLCA(arg1, arg2)];
                arg1 = DFSN[arg1];
                arg2 = DFSN[arg2];
                printf("%lld\n", root->querySum(arg1, arg1) +
                                     root->querySum(arg2, arg2) -
                                     2 * root->querySum(lca, lca) + 1);
            } else if (opt == 3) {
    
                printf("%lld\n", root->queryMax(DFSN[arg1], DFSN[arg1] + size[arg1] - 1));
            }
        }
        return 0;
    }

     

  • NOI2014 魔法森林

    把边按照一种权值排序,然后考虑一个奇怪的贪心。

    依次加边,成环的时候讨论一下

    当前边第二种权值大于环上第二种权值最大的边

    这种边留着也没用,扔了吧。

    当前边第二种权值小于环上第二种权值最大的边。

    加了或许有点用,于是删掉环上第二种权值最大的边,并把这条边连上去。

    每次加完边判一下连通性,更新答案即可。

    #include <assert.h>
    #include <algorithm>
    #include <cstring>
    #include <iostream>
    #include <vector>
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    const int_t LARGE = 2e5;
    const int_t INF = 0x7fffffff;
    std::ostream& operator<<(std::ostream& os, const struct Node& node);
    struct State {
        int_t val, id;
        State operator+(const State& x) const {
            if (val > x.val) return *this;
            return x;
        };
        static State E() { return State{-INF, INF}; }
    };
    struct Node {
        Node*& left;
        Node*& right;
        Node* chds[2] = {nullptr, nullptr};
        Node* parent = nullptr;
        int_t id;
        bool isEdge = false;
        State state;
        bool flipped = false;
        int_t weight, edgeID;
        Node() : left(chds[0]), right(chds[1]) {
            // this->max = -INF;
        }
        void flip() {
            this->flipped ^= 1;
            std::swap(left, right);
        }
    
        void pushDown() {
            if (left) {
                if (flipped) left->flip();
            }
            if (right) {
                if (flipped) right->flip();
            }
            flipped = false;
        }
        void updateFrom(Node* node) {
            state = node->state + state;
            node->parent = this;
        }
        void maintain() {
            state = State::E();
            if (isEdge) {
                state = State{weight, edgeID};
            }
            if (left) updateFrom(left);
            if (right) updateFrom(right);
        }
        int_t chdOf() const {
            if (parent == nullptr) return -1;
            if (this == parent->left)
                return 0;
            else if (this == parent->right)
                return 1;
            else
                return -1;
        }
    };
    struct Edge {
        int_t from;
        int_t to;
        int_t weight1;
        int_t weight2;
        bool operator<(const Edge& edge) const { return weight1 < edge.weight1; }
    } edges[LARGE + 1];
    Node* nodes[LARGE + 1];
    int n, m;
    // node 围绕父节点旋转
    void rotate(Node* node) {
        Node* Pr = node->parent;
        Pr->pushDown();
        node->pushDown();
        int_t side = node->chdOf();
        int_t sidep = Pr->chdOf();
        assert(side != -1);
        Node* Gr = Pr->parent;
        Pr->chds[side] = node->chds[side ^ 1];
        node->chds[side ^ 1] = Pr;
        node->parent = Gr;
        Pr->maintain();
        node->maintain();
        if (sidep != -1 && Gr) {
            Gr->chds[sidep] = node;
            Gr->maintain();
        }
    }
    //判断node是否是辅助树的根
    bool isAuxRoot(Node* node) {
        if (node == nullptr || node->parent == nullptr) return true;
        return node->chdOf() == -1;
    }
    void splay(Node* node) {
        static Node* stack[LARGE];
        int_t top = 0;
        Node* curr = node;
        while (true) {
            stack[top++] = curr;
            if (isAuxRoot(curr)) break;
            curr = curr->parent;
        }
        while (top) {
            stack[--top]->pushDown();
        }
    
        while (isAuxRoot(node) == false) {
            if (isAuxRoot(node->parent)) {
                rotate(node);
            } else {
                int_t p1 = node->chdOf();
                int_t p2 = node->chdOf();
                if (p1 == p2) {
                    rotate(node->parent);
                } else {
                    rotate(node);
                }
                rotate(node);
            }
        }
    }
    
    void access(Node* node) {
        Node* last = nullptr;
        while (node) {
            splay(node);
            node->right = last;
            last = node;
            node->maintain();
            node = node->parent;
        }
    }
    
    void changeRoot(Node* node) {
        access(node);
        splay(node);
        node->flip();
    }
    
    State query(Node* v1, Node* v2) {
        changeRoot(v1);
        access(v2);
        splay(v2);
        return v2->state;
    }
    Node* getRoot(Node* node) {
        access(node);
        splay(node);
        while (true) {
            node->pushDown();
    
            if (node->left == nullptr) break;
    #ifdef DEBUG
            cout << "left id " << node->left->id << endl;
    #endif
            node = node->left;
        }
        return node;
    }
    void link(Node* v1, Node* v2) {
        //连边一定一定要换根!!
        changeRoot(v2);
        access(v1);
        splay(v1);
        v1->pushDown();
        v1->right = v2;
        v1->maintain();
    }
    void cut(Node* v1, Node* v2) {
    #ifdef DEBUG
        cout << "cutting " << v1->id << " and " << v2->id << endl;
    #endif
        changeRoot(v1);
        access(v2);
        splay(v1);
        v1->right = nullptr;
        v2->parent = nullptr;
        v1->maintain();
    }
    std::ostream& operator<<(std::ostream& os, const Node& node) {
        const auto access = [&](const Node* x) -> int_t {
            if (x == nullptr) return -1;
            return x->id;
        };
        os << "Node{" << node.id << ",parent=" << access(node.parent)
           << ",left=" << access(node.left) << ",right=" << access(node.right)
           << ",flip=" << node.flipped << ",isedge=" << node.isEdge
           << ",edgeid=" << node.edgeID << ",val=" << node.weight
           << ",chdof=" << node.chdOf() << "}";
        return os;
    }
    int main() {
        // freopen("qwq.txt", "r", stdin);
        scanf("%d%d", &n, &m);
        for (int_t i = 1; i <= n + m; i++) {
            nodes[i] = new Node;
            nodes[i]->id = i;
        }
        for (int_t i = 1; i <= m; i++) {
            auto& curr = edges[i];
            scanf("%lld%lld%lld%lld", &curr.from, &curr.to, &curr.weight1,
                  &curr.weight2);
        }
        std::sort(edges + 1, edges + 1 + m);
        int_t result = INF;
        for (int_t i = 1; i <= m; i++) {
            auto& curr = edges[i];
    #ifdef DEBUG
    
            cout << "trying edge " << curr.from << "," << curr.to << ","
                 << curr.weight1 << "," << curr.weight2 << endl;
    #endif
            if (getRoot(nodes[curr.from]) == getRoot(nodes[curr.to])) {
    #ifdef DEBUG
                cout << "circle detected" << endl;
    #endif
                auto res = query(nodes[curr.from], nodes[curr.to]);
    #ifdef DEBUG
                cout << "maxedge id " << res.id << " len " << res.val << endl;
                for (int_t i = 1; i <= n + m; i++) {
                    cout << (*nodes[i]) << endl;
                }
    #endif
                if (res.val <= curr.weight2) continue;
                //断掉原有边的连接
                cut(nodes[edges[res.id].from], nodes[res.id + n]);
                cut(nodes[edges[res.id].to], nodes[res.id + n]);
            }
            //连边
            nodes[n + i]->weight = curr.weight2;
            nodes[n + i]->edgeID = i;
            nodes[n + i]->isEdge = true;
            nodes[n + i]->maintain();
            link(nodes[n + i], nodes[curr.from]);
            link(nodes[n + i], nodes[curr.to]);
            //更新答案
            if (getRoot(nodes[1]) == getRoot(nodes[n])) {
                result =
                    std::min(result, curr.weight1 + query(nodes[1], nodes[n]).val);
            }
        }
        if (result == INF) {
            result = -1;
        }
        printf("%lld\n", result);
        return 0;
    }

     

  • LNOI2014 LCA

    首先询问显然是可以差分的。

    然后考虑如何计算点$vtx$与点$z$的LCA的深度。

    让点$vtx$到树根的所有点的点权$+1$,然后查询$z$到树根的点权和,这个值就是$z$与$vtx$的LCA的深度。

    然后把每个询问拆成两个前缀和,然后按照询问端点排序,一个一个处理。

    处理点$vtx$的询问时,先把$vtx$到根的所有点权值+1,然后对于所有的$z$,查询$z$到树根的权值和即可。

    #include <assert.h>
    #include <algorithm>
    #include <cstring>
    #include <iostream>
    #include <vector>
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    const int_t mod = 201314;
    const int_t LARGE = 2e5;
    const int_t INF = 0x7fffffff;
    std::ostream& operator<<(std::ostream& os, const struct Node& node);
    struct Node {
        Node*& left;
        Node*& right;
        Node* chds[2] = {nullptr, nullptr};
        Node* parent = nullptr;
        int_t id;
        int_t value;
        int_t sum;
        int_t max;
        int_t mark = 0;
        int_t size = 1;
        bool flipped = false;
        Node(int_t val) : left(chds[0]), right(chds[1]) {
            this->value = this->sum = this->max = val;
            // this->max = -INF;
        }
        void flip() {
            this->flipped ^= 1;
            std::swap(left, right);
        }
        void add(int_t x) {
            this->mark += x;
            this->sum += size * x;
            this->max += x;
            value += x;
        }
        void pushDown() {
            if (left) {
                left->add(mark);
                if (flipped) left->flip();
            }
            if (right) {
                right->add(mark);
                if (flipped) right->flip();
            }
            flipped = false;
            mark = 0;
        }
        void updateFrom(Node* node) {
            size += node->size;
            sum += node->sum;
            max = std::max(max, node->max);
            node->parent = this;
        }
        void maintain() {
            max = sum = value;
            size = 1;
            if (left) updateFrom(left);
            if (right) updateFrom(right);
        }
        int_t chdOf() const {
            if (parent == nullptr) return -1;
            if (this == parent->left)
                return 0;
            else if (this == parent->right)
                return 1;
            else
                return -1;
        }
    };
    Node* nodes[LARGE + 1];
    int n, q;
    // node 围绕父节点旋转
    void rotate(Node* node) {
        Node* Pr = node->parent;
        Pr->pushDown();
        node->pushDown();
        int_t side = node->chdOf();
        int_t sidep = Pr->chdOf();
        assert(side != -1);
        Node* Gr = Pr->parent;
        Pr->chds[side] = node->chds[side ^ 1];
        node->chds[side ^ 1] = Pr;
        node->parent = Gr;
        Pr->maintain();
        node->maintain();
        if (sidep != -1 && Gr) {
            Gr->chds[sidep] = node;
            Gr->maintain();
        }
    }
    //判断node是否是辅助树的根
    bool isAuxRoot(Node* node) {
        if (node == nullptr || node->parent == nullptr) return true;
        return node->chdOf() == -1;
    }
    void splay(Node* node) {
        static Node* stack[LARGE];
        int_t top = 0;
        Node* curr = node;
        while (true) {
            stack[top++] = curr;
            if (isAuxRoot(curr)) break;
            curr = curr->parent;
        }
        while (top) {
            stack[--top]->pushDown();
        }
    
        while (isAuxRoot(node) == false) {
            if (isAuxRoot(node->parent)) {
                rotate(node);
            } else {
                int_t p1 = node->chdOf();
                int_t p2 = node->chdOf();
                if (p1 == p2) {
                    rotate(node->parent);
                } else {
                    rotate(node);
                }
                rotate(node);
            }
        }
    }
    
    void access(Node* node) {
        Node* last = nullptr;
        while (node) {
            splay(node);
            node->right = last;
            last = node;
            node->maintain();
            node = node->parent;
        }
    }
    
    void changeRoot(Node* node) {
        access(node);
        splay(node);
        node->flip();
    }
    
    int_t query(Node* v1, Node* v2) {
        changeRoot(v1);
        access(v2);
        splay(v2);
        return v2->sum;
    }
    void add(Node* v1, Node* v2, int_t val) {
        changeRoot(v1);
        access(v2);
        splay(v2);
        v2->add(val);
    }
    
    void link(Node* v1, Node* v2) {
        //连边一定一定要换根!!
        changeRoot(v2);
        access(v1);
        splay(v1);
        v1->pushDown();
        v1->right = v2;
        v1->maintain();
    }
    
    int main() {
        // freopen("qwq.txt", "r", stdin);
        static int_t result[LARGE + 1];
        struct Operation {
            int_t ID;
            int_t type;
            int_t z;
        };
        std::vector<Operation> querys[LARGE + 1];
        scanf("%d%d", &n, &q);
        for (int_t i = 1; i <= n; i++) {
            nodes[i] = new Node(0);
            nodes[i]->id = i;
        }
        for (int_t i = 2; i <= n; i++) {
            int_t parent;
            scanf("%lld", &parent);
            parent += 1;
            link(nodes[i], nodes[parent]);
        }
        for (int_t i = 1; i <= q; i++) {
            int_t left, right, z;
            scanf("%lld%lld%lld", &left, &right, &z);
            z++;
            left++;
            right++;
            querys[left - 1].push_back(Operation{i, -1, z});
            querys[right].push_back(Operation{i, 1, z});
        }
        for (int_t i = 1; i <= n; i++) {
            //路径上的点权+1
            add(nodes[1], nodes[i], 1);
            //处理这个端点的答案
            for (const auto& opt : querys[i]) {
                int_t x = opt.type * query(nodes[1], nodes[opt.z]);
                result[opt.ID] += x;
            }
        }
        for (int_t i = 1; i <= q; i++) {
            printf("%lld\n", result[i] % mod);
        }
        return 0;
    }

     

  • ZJOI2007 树的统计

    LCT练手题。

    #include <assert.h>
    #include <algorithm>
    #include <cstring>
    #include <iostream>
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    const int_t LARGE = 2e5;
    const int_t INF = 0x7fffffff;
    std::ostream& operator<<(std::ostream& os, const struct Node& node);
    struct Node {
        Node*& left;
        Node*& right;
        Node* chds[2] = {nullptr, nullptr};
        Node* parent = nullptr;
        int_t id;
        int_t value;
        int_t sum;
        int_t max;
        int_t mark = 0;
        int_t size = 1;
        bool flipped = false;
        Node(int_t val) : left(chds[0]), right(chds[1]) {
            this->value = this->sum = this->max = val;
            // this->max = -INF;
        }
        void flip() {
            this->flipped ^= 1;
            std::swap(left, right);
        }
        void add(int_t x) {
            this->mark += x;
            this->sum += size * x;
            this->max += x;
        }
        void pushDown() {
            if (left) {
                left->add(mark);
                if (flipped) left->flip();
            }
            if (right) {
                right->add(mark);
                if (flipped) right->flip();
            }
            flipped = false;
            mark = 0;
        }
        void updateFrom(Node* node) {
            size += node->size;
            sum += node->sum;
            max = std::max(max, node->max);
            node->parent = this;
        }
        void maintain() {
            max = sum = value;
            size = 1;
            if (left) updateFrom(left);
            if (right) updateFrom(right);
        }
        int_t chdOf() const {
            if (parent == nullptr) return -1;
            if (this == parent->left)
                return 0;
            else if (this == parent->right)
                return 1;
            else
                return -1;
        }
    };
    Node* nodes[LARGE + 1];
    int n, q;
    // node 围绕父节点旋转
    void rotate(Node* node) {
        Node* Pr = node->parent;
        Pr->pushDown();
        node->pushDown();
        int_t side = node->chdOf();
        int_t sidep = Pr->chdOf();
        assert(side != -1);
        Node* Gr = Pr->parent;
    #ifdef DEBUG
        cout << "rotating " << (*node) << endl;
        cout << "Pr=" << (*Pr) << endl;
    #endif
        Pr->chds[side] = node->chds[side ^ 1];
        node->chds[side ^ 1] = Pr;
        node->parent = Gr;
        Pr->maintain();
        node->maintain();
        if (sidep != -1 && Gr) {
            Gr->chds[sidep] = node;
            Gr->maintain();
        }
    }
    //判断node是否是辅助树的根
    bool isAuxRoot(Node* node) {
        if (node == nullptr || node->parent == nullptr) return true;
        return node->chdOf() == -1;
    }
    void splay(Node* node) {
        static Node* stack[LARGE];
        int_t top = 0;
        Node* curr = node;
    #ifdef DEBUG
        cout << "splaying " << (*node) << endl;
    #endif
        while (true) {
    #ifdef DEBUG
            cout << "pushing " << (*curr) << "into stack" << endl;
    #endif
            stack[top++] = curr;
            if (isAuxRoot(curr)) break;
            curr = curr->parent;
        }
        while (top) {
            stack[--top]->pushDown();
        }
    
        while (isAuxRoot(node) == false) {
            if (isAuxRoot(node->parent)) {
                rotate(node);
            } else {
                int_t p1 = node->chdOf();
                int_t p2 = node->chdOf();
                if (p1 == p2) {
                    rotate(node->parent);
                } else {
                    rotate(node);
                }
                rotate(node);
            }
        }
    }
    
    void access(Node* node) {
        Node* last = nullptr;
        while (node) {
            splay(node);
            node->right = last;
            last = node;
            node->maintain();
            node = node->parent;
        }
    }
    
    void changeRoot(Node* node) {
        access(node);
        splay(node);
        node->flip();
    }
    
    void query(Node* v1, Node* v2, int_t& max, int_t& sum) {
    #ifdef DEBUG
        cout << "querying " << v1->id << "," << v2->id << endl;
    #endif
        changeRoot(v1);
        access(v2);
        splay(v2);
        max = v2->max;
        sum = v2->sum;
    #ifdef DEBUG
        cout << (*v2) << endl;
        for (int_t i = 1; i <= n; i++) cout << (*nodes[i]) << endl;
    #endif
    }
    void add(Node* v1, Node* v2, int_t val) {
        changeRoot(v1);
        access(v2);
        splay(v2);
        v2->add(val);
    }
    void set(Node* node, int_t val) {
        // access(node);
        splay(node);
        node->pushDown();
        node->value = val;
        node->maintain();
    }
    std::ostream& operator<<(std::ostream& os, const Node& node) {
        const auto access = [&](const Node* x) -> int_t {
            if (x == nullptr) return -1;
            return x->id;
        };
        os << "Node{" << node.id << ",parent=" << access(node.parent)
           << ",left=" << access(node.left) << ",right=" << access(node.right)
           << ",flip=" << node.flipped << ",sum=" << node.sum << ",max=" << node.max
           << ",chdof=" << node.chdOf() << "}";
        return os;
    }
    void link(Node* v1, Node* v2) {
    #ifdef DEBUG
        cout << "Linking " << (*v1) << " " << (*v2) << endl;
    #endif
        //连边一定一定要换根!!
        changeRoot(v2);
        access(v1);
        splay(v1);
        v1->pushDown();
        v1->right = v2;
        v1->maintain();
    }
    int main() {
        // freopen("1.in", "r", stdin);
        scanf("%d", &n);
        for (int_t i = 1; i <= n; i++) {
            nodes[i] = new Node(0);
            nodes[i]->id = i;
        }
        for (int_t i = 1; i <= n - 1; i++) {
            int from, to;
            scanf("%d%d", &from, &to);
            link(nodes[from], nodes[to]);
        }
    
        for (int_t i = 1; i <= n; i++) {
            int_t val;
            scanf("%lld", &val);
            set(nodes[i], val);
        }
        scanf("%d", &q);
        for (int_t i = 1; i <= q; i++) {
            static char buf[20];
            int arg1, arg2;
            scanf("%s%d%d", buf, &arg1, &arg2);
            if (strcmp(buf, "CHANGE") == 0) {
                set(nodes[arg1], arg2);
            } else {
                int_t max, sum;
                query(nodes[arg1], nodes[arg2], max, sum);
                if (strcmp("QMAX", buf) == 0) {
                    printf("%lld\n", max);
                } else if (strcmp("QSUM", buf) == 0) {
                    printf("%lld\n", sum);
                }
            }
        }
        return 0;
    }

     

  • 洛谷3690 LCT模板

    被卡的真厉害..

    #pragma GCC optimize("Ofast")
    #include <iostream>
    #include <cstdio>
    #include <string>
    #include <algorithm>
    #include <vector>
    #include <utility>
    #include <assert.h>
    using int_t = int;
    using pair_t = std::pair<struct Node *, struct Node *>;
    using std::cin;
    using std::cout;
    using std::endl;
    #ifndef DEBUG
    #define assert(cond) ;
    #endif
    
    const int_t LARGE = 300000;
    std::ostream &operator<<(std::ostream &os, const Node &node);
    void link(Node *v1, Node *v2);
    void changeRoot(Node *node);
    void access(Node *node);
    Node *splay(Node *root);
    bool isRoot(Node *node);
    Node *getRoot(Node *node);
    struct Node
    {
        Node *parent = nullptr;
        Node *chds[2];
        Node *&left = chds[0];
        Node *&right = chds[1];
        int_t value;
        int_t sum = 0;
        int_t size = 1;
        bool flipped = false;
        Node(int_t value)
        {
            this->value = value;
            left = right = nullptr;
        }
        void updateFrom(Node *node)
        {
            assert(node != this);
            size += node->size;
            sum ^= node->sum;
            node->parent = this;
        }
        void maintain()
        {
            if (left)
                assert(left != parent);
            if (right)
                assert(right != parent);
            size = 1;
            sum = value;
            if (left)
                updateFrom(left);
            if (right)
                updateFrom(right);
        }
        void flip()
        {
            flipped ^= 1;
            std::swap(left, right);
        }
        void pushdown()
        {
            if (flipped)
            {
                if (left)
                    left->flip();
                if (right)
                    right->flip();
                flipped ^= 1;
            }
        }
        int_t chdOf() const
        {
            if (parent == nullptr)
                return -1;
            if (this == parent->left)
                return 0;
            else if (this == parent->right)
                return 1;
            return -1;
        }
        int_t mid()
        {
            // cout << "(mid at " << value << ")" << endl;
            pushdown();
            if (left)
                left->mid();
            cout << value << " ";
            if (right)
                right->mid();
            maintain();
            return size;
        }
        int_t leftSize()
        {
            if (left == nullptr)
                return 0;
            return left->size;
        }
    };
    
    //node绕其父节点进行旋转
    //返回旋转后的父节点
    Node *rotate(Node *node)
    {
        Node *Pr = node->parent;
    #ifdef DEBUG
        cout << "rotating " << (*node) << " parent = " << (*Pr) << endl;
    #endif
        //先pushdown,再获取信息
        Pr->pushdown();
        node->pushdown();
        int_t side = node->chdOf();
        assert(side != -1);
        int_t sidep = Pr->chdOf();
        Node *oldPar = Pr->parent;
        Pr->chds[side] = node->chds[side ^ 1];
        node->chds[side ^ 1] = Pr;
        //别忘了设置父节点
        node->parent = oldPar;
        Pr->parent = node;
        Pr->maintain();
        node->maintain();
        if (sidep != -1 && oldPar)
        {
            oldPar->chds[sidep] = node;
            oldPar->maintain();
        }
    
        return node;
    }
    //获取node对应的树的根
    Node *getRoot(Node *node)
    {
        access(node);
        while (node->left)
        {
            node->pushdown();
            node = node->left;
        }
        // splay(node);
        return node;
    }
    //判断node是否是辅助树的根
    
    bool isRoot(Node *node)
    {
        if (node == nullptr)
            return true;
        return node->chdOf() == -1;
    }
    
    //把root旋转到当前辅助树树根
    
    Node *splay(Node *root)
    {
    #ifdef DEBUG
        cout << "splaying " << (*root) << endl;
    #endif
        static Node *stack[LARGE + 1];
        int_t top = 0;
        Node *node = root;
        while (isRoot(node) == false)
        {
            stack[top++] = node;
            node = node->parent;
        }
        // nodes.push_back(node);
        stack[top++] = node;
        while (top > 0)
        {
            stack[--top]->pushdown();
        }
        while (isRoot(root) == false)
        {
            if (isRoot(root->parent))
            {
                rotate(root);
            }
            else
            {
                if (root->chdOf() == root->parent->chdOf())
                {
                    rotate(root->parent);
                }
                else
                {
                    rotate(root);
                }
                rotate(root);
            }
        }
        return root;
    }
    
    void access(Node *node)
    {
    #ifdef DEBUG
        cout << "Accessing " << (*node) << endl;
        Node *_ = node;
    #endif
        Node *last = nullptr;
        while (node)
        {
            //转到根
            splay(node);
            //接上上一棵树
            node->right = last;
    #ifdef DEBUG
    
            if (last)
            {
                cout << "connecting " << (*last) << " to " << (*node) << endl;
            }
    #endif
            last = node;
            node->maintain();
            node = node->parent;
        }
    #ifdef DEBUG
        assert(_->right == nullptr);
    #endif
    }
    //让node变成整棵树的根
    
    void changeRoot(Node *node)
    {
        access(node);
        splay(node);
        node->flip();
    }
    //把v2连接到v1上
    void link(Node *v1, Node *v2)
    {
        changeRoot(v2);
        access(v1);
        // splay(v1);
    
    #ifdef DEBUG
        cout << "linking " << (*v1) << " and " << (*v2) << endl;
        cout << "root of v1 = " << (*getRoot(v1)) << endl;
    #endif
        //既然要把v2接到v1上,那么就要保证v1这棵树的树根不是v2(v2已经是他所在的树的树根了)
        if (v2 != getRoot(v1))
        {
            v1->pushdown();
            v2->parent = v1;
            v1->maintain();
            splay(v2);
        }
    }
    void cut(Node *v1, Node *v2)
    {
        changeRoot(v1);
        access(v2);
        splay(v1);
        if (v1->right == v2 && v1->size == 2)
        {
            v2->parent = nullptr;
            v1->right = nullptr;
            v1->maintain();
            splay(v2);
        }
    }
    void modify(Node *node, int_t val)
    {
        access(node);
        splay(node);
        node->value = val;
        node->maintain();
    }
    std::ostream &operator<<(std::ostream &os, const Node &node)
    {
        const auto access = [&](const Node *x) -> int_t {
            if (x == nullptr)
                return -1;
            return x->value;
        };
        os << "Node{" << node.value << ",parent=" << access(node.parent) << ",left=" << access(node.left) << ",right=" << access(node.right) << ",flip=" << node.flipped << ",sum=" << node.sum << ",chdof=" << node.chdOf() << "}";
        return os;
    }
    int main()
    {
        static char buff[LARGE * 2 * sizeof(Node)];
        int_t used = 0;
        static Node *nodes[LARGE + 1];
        int_t n, m;
        cin >> n >> m;
        for (int_t i = 1; i <= n; i++)
        {
            int_t val;
            cin >> val;
            nodes[i] = new (buff + (used++) * sizeof(Node)) Node(val);
        }
        for (int_t i = 1; i <= m; i++)
        {
            int_t opt, x0, y0;
            cin >> opt >> x0 >> y0;
    
            if (opt == 0)
            {
                Node *v1 = nodes[x0], *v2 = nodes[y0];
                changeRoot(v1);
                access(v2);
                splay(v2);
                cout << v2->sum << endl;
            }
            else if (opt == 1)
            {
                Node *v1 = nodes[x0], *v2 = nodes[y0];
                link(v2, v1);
            }
            else if (opt == 2)
            {
                Node *v1 = nodes[x0], *v2 = nodes[y0];
                cut(v1, v2);
            }
            else if (opt == 3)
            {
                modify(nodes[x0], y0);
            }
        }
        return 0;
    }