結果

問題 No.2676 A Tourist
ユーザー NokonoKotlinNokonoKotlin
提出日時 2024-02-11 20:07:19
言語 C++23
(gcc 12.3.0 + boost 1.83.0)
結果
AC  
実行時間 3,881 ms / 5,000 ms
コード長 8,842 bytes
コンパイル時間 2,175 ms
コンパイル使用メモリ 169,564 KB
実行使用メモリ 46,336 KB
最終ジャッジ日時 2024-09-29 23:09:18
合計ジャッジ時間 39,767 ms
ジャッジサーバーID
(参考情報)
judge4 / judge2
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 2 ms
5,248 KB
testcase_01 AC 694 ms
17,152 KB
testcase_02 AC 2,398 ms
45,568 KB
testcase_03 AC 1,708 ms
45,568 KB
testcase_04 AC 1,851 ms
45,568 KB
testcase_05 AC 1,986 ms
45,568 KB
testcase_06 AC 553 ms
17,536 KB
testcase_07 AC 1,901 ms
46,208 KB
testcase_08 AC 392 ms
46,336 KB
testcase_09 AC 1,369 ms
46,336 KB
testcase_10 AC 496 ms
46,208 KB
testcase_11 AC 1,148 ms
42,624 KB
testcase_12 AC 584 ms
17,152 KB
testcase_13 AC 1,741 ms
45,568 KB
testcase_14 AC 970 ms
45,568 KB
testcase_15 AC 1,304 ms
45,440 KB
testcase_16 AC 598 ms
17,280 KB
testcase_17 AC 2,013 ms
45,440 KB
testcase_18 AC 1,320 ms
45,568 KB
testcase_19 AC 1,860 ms
45,440 KB
testcase_20 AC 1,657 ms
45,440 KB
testcase_21 AC 87 ms
5,248 KB
testcase_22 AC 151 ms
5,248 KB
testcase_23 AC 94 ms
5,248 KB
testcase_24 AC 179 ms
5,248 KB
testcase_25 AC 33 ms
5,248 KB
testcase_26 AC 2 ms
5,248 KB
testcase_27 AC 239 ms
17,476 KB
testcase_28 AC 797 ms
46,232 KB
testcase_29 AC 355 ms
46,236 KB
testcase_30 AC 719 ms
46,236 KB
testcase_31 AC 613 ms
46,240 KB
testcase_32 AC 3,881 ms
45,568 KB
権限があれば一括ダウンロードができます

ソースコード

diff #

#include <cassert>
#include <cmath>
#include <iostream>
#include <stack>
#include <vector>
using namespace std;

#define FOR(i, a, b) for (long long i = (a); i < (b); ++i)
#define REP(i, n) FOR(i, 0, n)

template <class T>
class LinkCutTree {
   public:
    struct LinkCutNode {
        LinkCutNode *parent = nullptr;
        LinkCutNode *left = nullptr;
        LinkCutNode *right = nullptr;
        T Value;
        T Sum;
        int id = -1;
        int SubTreeSize = 1;
        bool reverse_flag_lazy = false;
        void set_lazyReverse() {
            this->reverse_flag_lazy = !(this->reverse_flag_lazy);
        }
        LinkCutNode() {
        }
        LinkCutNode(int id_, T val) {
            id = id_;
            Value = val;
            Sum = val;
        }
        bool isExist(LinkCutNode *Nd) {
            if (Nd != nullptr)
                return true;
            else
                return false;
        }

        bool isRoot() {
            return (isExist(this->parent) == false || (this->parent->left != this && this->parent->right != this));
        }

        void rotate() {
            LinkCutNode *Parent, *GrandParent, *Child;
            Parent = this->parent;

            Child = nullptr;
            if (isExist(Parent) == false) {
                return;
            }
            GrandParent = Parent->parent;

            Parent->eval();
            this->eval();
            if (Parent->left == this) {
                Child = this->right;
                this->right = Parent;
                Parent->left = Child;
            } else if (Parent->right == this) {
                Child = this->left;
                this->left = Parent;
                Parent->right = Child;
            }
            if (isExist(GrandParent)) {
                if (Parent == GrandParent->left) {
                    GrandParent->left = this;
                } else if (Parent == GrandParent->right) {
                    GrandParent->right = this;
                }
            }
            this->parent = GrandParent;
            Parent->parent = this;
            if (isExist(Child)) Child->parent = Parent;
            Parent->update();
            this->update();
            return;
        }

        int state() {
            if (isExist(this->parent) == false) {
                return 0;
            } else {
                this->parent->eval();
                if (this->parent->left == this) {
                    return 1;
                } else if (this->parent->right == this) {
                    return 2;
                } else {
                    return 0;
                }
            }
        }

        void splay() {
            if (isExist(this) == false) return;
            this->eval();

            while (this->state() != 0) {
                if (this->parent->state() == 0) {
                    this->rotate();
                } else if (this->state() == this->parent->state()) {
                    this->parent->rotate();
                    this->rotate();
                } else {
                    this->rotate();
                    this->rotate();
                }
            }
            return;
        }

        void update(bool evaluate = true) {
            if (isExist(this) == false) return;

            if (evaluate) this->eval();
            if (isExist(this->left) && evaluate) this->left->eval();
            if (isExist(this->right) && evaluate) this->right->eval();
            this->SubTreeSize = 1;
            this->Sum = this->Value;
            if (isExist(this->left)) {
                this->SubTreeSize += this->left->SubTreeSize;
                this->Sum += this->left->Sum;
            }
            if (isExist(this->right)) {
                this->SubTreeSize += this->right->SubTreeSize;
                this->Sum += this->right->Sum;
            }

            return;
        }
        void eval() {
            if (this->reverse_flag_lazy) {
                swap(this->left, this->right);
                // 下に伝播
                if (isExist(this->left)) this->left->set_lazyReverse();
                if (isExist(this->right)) this->right->set_lazyReverse();
                this->reverse_flag_lazy = false;
            }
        }
    };

   private:
    bool isExist(LinkCutNode *Nd) {
        if (Nd == nullptr) {
            return false;
        } else {
            return true;
        }
    }

    int access_sub(LinkCutNode *Node) {
        if (isExist(Node) == false) return -1;
        int res = Node->id;
        while (1) {
            Node->splay();
            Node->right = nullptr;
            Node->update();
            if (isExist(Node->parent) == false) break;
            res = Node->parent->id;

            Node->parent->splay();
            Node->parent->right = Node;
            Node->parent->update();
        }
        return res;
    }

    void link_sub(LinkCutNode *c, LinkCutNode *p) {
        if (isExist(c) && isExist(p)) {
            evert_sub(c);
            access_sub(p);
            c->parent = p;
            p->update();
        }
    }

    void evert_sub(LinkCutNode *Node) {
        if (isExist(Node) == false) return;
        access_sub(Node);
        Node->set_lazyReverse();
        Node->update();
        access_sub(Node);
    }

   public:
    int V = 0;

    vector<LinkCutNode *> Nodes;

    LinkCutTree(int size_, T init_ = 0) {
        V = size_ + 10;
        Nodes = vector<LinkCutNode *>(V);
        REP(i, V)
        Nodes[i] = new LinkCutNode(i, init_);
    }

    int access(int id) {
        return access_sub(Nodes[id]);
    }

    void update_val(int i, T x) {
        access(i);
        Nodes[i]->Value = x;
        Nodes[i]->update();
    }

    void add_val(int i, T x) {
        access(i);
        Nodes[i]->Value += x;
        Nodes[i]->update();
    }

    void evert(int x) {
        evert_sub(Nodes[x]);
    }

    void link(int c, int p) {
        link_sub(Nodes[c], Nodes[p]);
    }

    int LCA(int x, int y) {
        access(x);
        int lca = access(y);
        access(lca);
        return lca;
    }

    void debug() {
        for (LinkCutNode *x : Nodes) {
            cerr << x->id << "th node : " << x->Value << endl;
        }
    }
};

int main() {
    int N, Q;
    cin >> N >> Q;
    vector<vector<int> > G(N + 1);

    // パス上の頂点の重みを計算する
    LinkCutTree<long long> T(N + 1, 0);

    // 隣接頂点の重みの和(ただし、更新クエリでは 次数が border 以上の頂点の変更を反映しない)
    LinkCutTree<long long> T_adj(N + 1, 0);

    vector<long long> a(N + 1);

    for (int i = 0; i < N; i++) {
        cin >> a[i + 1];
        T.update_val(i + 1, a[i + 1]);
    }

    for (int i = 0; i < N - 1; i++) {
        int u, v;
        cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);
        T.link(u, v);
        T_adj.link(u, v);
    }

    T.evert(1);  // 根頂点を 1 に
    T_adj.evert(1);

    for (int u = 1; u < N + 1; u++) {
        for (int nx : G[u]) T_adj.add_val(nx, T.Nodes[u]->Value);
    }

    int border = sqrt(N);
    vector<int> stack_vertices;        // 次数が大きい頂点たち
    vector<long long> diff(N + 1, 0);  // 更新クエリによる、元の重みとの差分

    for (int x = 1; x <= N; x++) {
        if (int(G[x].size()) >= border) stack_vertices.push_back(x);
    }

    for (int c = 1; c <= Q; c++) {
        long long t, u, v;
        cin >> t >> u >> v;
        if (t == 0) {
            // 次数が大きい頂点のデータに関しては、クエリに答えるときに計算する
            if (int(G[u].size()) >= border) {
                diff[u] += v;
                continue;
            }

            // 次数が小さいものは愚直に更新する
            T.add_val(u, v);
            if (int(G[u].size()) < border) {
                for (int adj : G[u]) {
                    T_adj.add_val(adj, v);
                }
            }
        } else {
            long long res = 0;
            T.evert(u);  // 根頂点を u にすることで、v にアクセスするとパスになる
            T.access(v);
            T_adj.evert(u);
            T_adj.access(v);

            res = T_adj.Nodes[v]->Sum - T.Nodes[v]->Sum + T.Nodes[u]->Value + T.Nodes[v]->Value;

            for (int x : stack_vertices) {
                T.evert(x);
                int lca = T.LCA(u, v);
                T.access(lca);
                // 頂点 x の隣接頂点が パス u-v に影響がある場合
                if (T.Nodes[lca]->SubTreeSize - 1 == 1) res += diff[x];
                // 頂点 x 地震が パス u-v に影響がある場合
                if (T.Nodes[lca]->SubTreeSize - 1 == 0) res += diff[x];
            }
            cout << res << "\n";
        }
    }

    return 0;
}
0