博客

  • CF1009F Dominant Indices

    直接长链剖分优化。

    一个点的答案直接从他的重子节点拖过来,然后暴力合并上其他非重子节点的答案。

    每条长链开个数组即可。

    复杂度为所有长链的长度和,$O(n)$。

    #include <iostream>
    #include <vector>
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    
    const int_t LARGE = 1e6;
    int_t depth[LARGE + 1], maxChd[LARGE + 1], maxDepth[LARGE + 1];
    std::vector<int_t> graph[LARGE + 1];
    int_t results[LARGE + 1];
    void DFS1(int_t vtx, int_t from = -1) {
        if (from == -1) depth[vtx] = 1;
        maxDepth[vtx] = depth[vtx];
        for (int_t to : graph[vtx])
            if (to != from) {
                depth[to] = depth[vtx] + 1;
                DFS1(to, vtx);
                maxDepth[vtx] = std::max(maxDepth[vtx], maxDepth[to]);
                if (maxDepth[to] > maxDepth[maxChd[vtx]]) {
                    maxChd[vtx] = to;
                }
            }
    }
    //当前链使用的状态数组为arr
    void DFS2(int_t vtx, int_t from, int_t* arr) {
        arr[0] += 1;
        int_t& result = ::results[vtx];
        //递归重子节点
        if (maxChd[vtx]) {
            DFS2(maxChd[vtx], vtx, arr + 1);
            result = results[maxChd[vtx]] + 1;
        }
        //递归处理其他子节点
        for (int_t to : graph[vtx]) {
            if (to == from || to == maxChd[vtx]) continue;
            int_t* next = new int_t[maxDepth[to] - depth[vtx] + 1];
            std::fill(next, next + maxDepth[to] - depth[vtx] + 1, 0);
            DFS2(to, vtx, next + 1);
            for (int_t i = 1; i <= maxDepth[to] - depth[vtx]; i++) {
                arr[i] += next[i];
                if (arr[i] > arr[result]) {
                    result = i;
                } else if (arr[i] == arr[result] && i < result)
                    result = i;
            }
        }
        if (arr[result] == 1) result = 0;
    }
    int n;
    int main() {
        scanf("%d", &n);
        for (int i = 1; i <= n - 1; i++) {
            int from, to;
            scanf("%d%d", &from, &to);
            graph[from].push_back(to);
            graph[to].push_back(from);
        }
        DFS1(1);
    #ifdef DEBUG
        for (int_t i = 1; i <= n; i++)
            cout << "maxChd " << i << " = " << maxChd[i] << endl;
    #endif
        auto ptr = new int_t[n + 1];
        std::fill(ptr, ptr + n + 1, 0);
        DFS2(1, -1, ptr);
        for (int i = 1; i <= n; i++) {
            printf("%d\n", (int)results[i]);
        }
        return 0;
    }

     

  • 十二省联考2019 春节(清明)十二响

    把你骨灰都给你扬了!

    真是送分题…可惜因为心态原因考场上想出来正解了都没敢写..

    首先考虑链的情况怎么做?

    既然根不是链的端点,那么这条链一定分为两部分,把两部分的所有权值扔堆里,每次各拿出来两部分的最大值进行配对,然后扔到结果里。

    最后加起来即可。

    推广到树上呢?

    很自然的想到写个DFS,这个DFS返回一个堆,表示这棵子树的答案。

    然后对于一个点把他的子节点的堆拿出来每次取出所有的最大值合并

    这样也是正确的,可惜时间复杂度和空间复杂度都可以被卡到$O(n^2)$。

    怎么优化呢?考虑到一个到子树内叶子节点最远距离为x的点,堆里只会有x个取值。

    所以我们可以长链剖分。

    对于一个点,我们优先递归它的深子节点,然后把其他的子节点的答案合并到这个深子节点的堆上。

    具体实现就是对于一条长链顶,我们新建一个堆,然后这条长链都使用这一个堆。

    当从长链顶返回时,合并完答案后,删除这个堆即可。

    时间复杂度为所有长链长度之和乘上堆的复杂度,即$O(nlogn)$。

    #include <assert.h>
    #include <iostream>
    #include <numeric>
    #include <queue>
    #include <set>
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    
    using heap_t = std::multiset<int_t, std::greater<int_t>>;
    
    const int_t LARGE = 2e5;
    int_t maxChd[LARGE + 1], depth[LARGE + 1], maxDepth[LARGE + 1];
    int_t vals[LARGE + 1];
    int_t n;
    std::vector<int_t> graph[LARGE + 1];
    
    void DFS1(int_t vtx) {
        maxDepth[vtx] = depth[vtx];
        for (int_t to : graph[vtx]) {
            depth[to] = depth[vtx] + 1;
            DFS1(to);
            maxDepth[vtx] = std::max(maxDepth[vtx], maxDepth[to]);
        }
        for (int_t to : graph[vtx]) {
            if (maxDepth[to] > maxDepth[maxChd[vtx]]) {
                maxChd[vtx] = to;
            }
        }
        if (maxChd[vtx] == vtx) maxChd[vtx] = 0;
    }
    void DFS2(int_t vtx, heap_t* heap) {
        //先递归重子节点
        if (maxChd[vtx]) DFS2(maxChd[vtx], heap);
        //递归非重子节点
        std::vector<heap_t*> heaps;
        for (int_t to : graph[vtx]) {
            if (to != maxChd[vtx]) {
                auto next = new heap_t;
                DFS2(to, next);
                heaps.push_back(next);
            }
        }
        std::vector<int_t> result;
        //合并答案
        while (true) {
            bool hasNonEmpty = false;
            int_t max = 0;
            for (auto ptr : heaps) {
                if (ptr->empty() == false) {
                    max = std::max(max, *(ptr->begin()));
                    ptr->erase(ptr->begin());
                    hasNonEmpty = true;
                }
            }
            if (hasNonEmpty == false) break;
            assert(heap->empty() == false);
            if (heap->empty() == false) {
                auto ptr = heap->begin();
                max = std::max(max, *ptr);
                heap->erase(ptr);
            }
            result.push_back(max);
        }
        for (auto ptr : heaps) {
            delete ptr;
        }
        result.push_back(vals[vtx]);
        for (int_t x : result) heap->insert(x);
    }
    int main() {
        scanf("%lld", &n);
        for (int_t i = 1; i <= n; i++) scanf("%lld", &vals[i]);
        for (int_t i = 2; i <= n; i++) {
            int_t x;
            scanf("%lld", &x);
            graph[x].push_back(i);
        }
        depth[1] = 1;
        DFS1(1);
        auto heap = new heap_t;
        DFS2(1, heap);
        int_t result = std::accumulate(heap->begin(), heap->end(), 0ll);
        cout << result << endl;
        return 0;
    }

     

  • 十二省联考2019 字符串问题

    没看题解给做出来了,说明自己还有点水平。

    先考虑下前40分怎么搞一个$O(n_a\times n_b)$的算法出来。

    我们可以枚举每一个B串,然后看一下这个B串是哪些A串的前缀,设以B串$b_0$为前缀的A串集合为$A(b_0)$。

    我们对于每一个A串建立一个点,设第$i$个A串对应的点为$a_i$,点权为这个A串的长度。

    对于一条支配关系$(x,y)$,我们把$a_x$向$A(b_y)$中的所有点连边。

    最后新建一个起点,这个起点向所有的A串的点连边,然后跑以这个点为起点的最长路即可。

    如果遇到环,那么这个环一定是正环(我们没有建立0权点),就说明存在无限长的合法串。

    如何判断一个$B$串是不是另一个$A$串的前缀?哈希。

    复杂度$O(n_a\times n_b)$,没有任何优化的空间。

    40分。

    可惜在考场上我连这40分都没有。

    考虑换一种思路,比如后缀数组?

    搞出来串S的后缀数组,定位到一个B串是哪一个后缀的前缀,然后直接二分出来往左往右到哪个位置仍然是以这个B串为前缀,然后向这个区间内长度比这个B串长的A串连边。

    直接做还是$O(n^2)$的,主席树优化建图可以搞到$O(nlogn)$。

    然而我不会后缀数组

    再换一种思路,比如SAM?

    后缀链接树上,任何一个点表示的字符串都一定是它子树内的点表示的串的严格后缀。

    然而我们现在要求出来有哪些A串是一个B串的前缀。

    所以把S串翻转一下,就变成了哪些A串是一个B串的后缀了。

    我们通过树上倍增的方式在后缀链接树上定位到所有的A串和B串。

    一个B串被挂到了点vtx上,就说明vtx上长度不小于A的串,和vtx子节点里面的所有串,都以B为后缀。

    我们再把所有的A串挂到后缀链接树上,然后对于每个A串新建一个两个点v1和v2,v2的点权为这个A串的长度,同时v1有向v2的边。

    然后假设点vtx有A串被挂上去了,那么首先把vtx拆成in和out两个点,out的出边为原先vtx的出边,in的入边为原先vtx的入边,同时in向out连一条边。

    对于挂在vtx上的每一个A串,我们把他们按照长度从小到大排序,设vtx上的第i个A串为$vtx_A(i)$,那么我们首先把in向$vtx_A(i).v1$连边(表示走到这个点后可以选择这个A

    串),然后把$vtx_A(i).v1$向$vtx_A(i+1).v1$连边,代表选择了一个长度小的A串后,选择长度比它大的也一定合法。

    然后再考虑B串,设点$vtx$上的第$i$个B串为$vtx_B(i)$,我们仍然建立点$v1$和$v1$,然后找到长度大于等于vtx_B(i)且在同一个点上的长度最小的A串$a_0$,然后从$vtx_B(i).v1$向$a_0.v1$连边,代表这个B串是这个点上长度大于等于它的A串的后缀,然后$v2$向$vtx.out$连边,代表这个B串是子树内所有A串的后缀。

    然后考虑支配关系,对于一个支配关系$(a,b)$,定位到A所对应的$v2$点和b所对应的$v1$点(表示从A串a下面可以接以B串b为后缀的所有A串),连边即可。

    然后跑DAG上的最长路,如果存在环说明有无限长的合法串。

    树上倍增不要写错了,注意树高的处理。

    倍增的2的幂的上界为$ceil(maxdepth)$,其中maxdepth为树最深的深度,从0开始。

    #include <assert.h>
    #include <inttypes.h>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <iostream>
    #include <set>
    #include <unordered_map>
    #include <vector>
    using int_t = int;
    using std::cin;
    using std::cout;
    using std::endl;
    const int_t LARGE = 5e5;
    void* allocate();
    int_t id = 0;
    struct Node {
        int_t sufID;
        int_t max;
        Node* link = nullptr;
        Node* chds[26];
        int_t id;
        Node(int_t max) {
            this->max = max;
            memset(chds, 0, sizeof(chds));
            sufID = -1;
            id = ++::id;
        }
        Node*& access(char chr) { return chds[chr - 'a']; }
        Node* clone() {
            Node* next = new (allocate()) Node(*this);
            next->id = ++::id;
            next->sufID = -1;
            return next;
        }
    };
    
    int_t used = 0;
    char buf[LARGE * 10 * sizeof(Node)];
    void* allocate() { return buf + (used++) * sizeof(Node); }
    Node* append(Node* root, Node* last, char chr, int_t sufID) {
        Node* next = new (allocate()) Node(last->max + 1);
        next->sufID = sufID;
        Node* curr = last;
        while (curr && curr->access(chr) == nullptr) {
            curr->access(chr) = next;
            curr = curr->link;
        }
        if (curr == nullptr) {
            next->link = root;
        } else if (curr->access(chr)->max == curr->max + 1) {
            next->link = curr->access(chr);
        } else {
            Node* oldNode = curr->access(chr);
            Node* newNode = oldNode->clone();
            oldNode->link = next->link = newNode;
            newNode->max = curr->max + 1;
            while (curr && curr->access(chr) == oldNode) {
                curr->access(chr) = newNode;
                curr = curr->link;
            }
        }
        return next;
    }
    char str[LARGE + 10];
    //某个后缀对应的SAM节点
    Node* sufs[LARGE + 1];
    int_t len, na, nb, m;
    Node* root;
    int_t jump[25][LARGE + 1], depth[LARGE + 1];
    int_t val[LARGE * 10 + 1];
    Node* nodes[LARGE + 1];
    std::vector<int_t> graph[LARGE * 10 + 1];
    void DFS0(int_t vtx) {
        for (int_t to : graph[vtx]) {
            jump[0][to] = vtx;
            depth[to] = depth[vtx] + 1;
            DFS0(to);
        }
    }
    //某个节点向上跳2^k到哪里
    void DFS1(int_t vtx, int_t k) {
        if ((1 << k) > depth[vtx])
            jump[k][vtx] = -1;
        else {
            jump[k][vtx] = jump[k - 1][jump[k - 1][vtx]];
        }
        for (int_t to : graph[vtx]) DFS1(to, k);
    }
    bool inStack[LARGE * 10 + 1];
    int64_t memory[LARGE * 10 + 1];
    bool visited[LARGE * 10 + 1];
    int64_t DFS(int_t vtx) {
        if (inStack[vtx]) return -1;
        if (visited[vtx]) return memory[vtx];
        inStack[vtx] = true;
        auto& result = memory[vtx];
        result = val[vtx];
        visited[vtx] = true;
        for (auto to : graph[vtx]) {
            auto x = DFS(to);
            if (x == -1) return -1;
            result = std::max(result, val[vtx] + x);
        }
        inStack[vtx] = false;
        return result;
    }
    int64_t process() {
        scanf("%s", str + 1);
        len = strlen(str + 1);
        Node* last = root = new (allocate()) Node(0);
        for (int_t i = len; i >= 1; i--) {
            last = append(root, last, str[i], i);
            sufs[i] = last;
        }
    
        //构建后缀树
        for (int_t i = 1; i < id; i++) {
            auto ptr = (Node*)(buf + sizeof(Node) * i);
            graph[ptr->link->id].push_back(ptr->id);
        }
        for (int_t i = 0; i < used; i++) {
            Node* ptr = (Node*)(buf + i * sizeof(Node));
            nodes[ptr->id] = ptr;
        }
    
        depth[1] = 0;
        DFS0(root->id);
        int_t maxdepth = *std::max_element(depth + 1, depth + 1 + id);
        const int_t SIZE2 = ceil(log2(maxdepth));
        for (int_t i = 1; i <= SIZE2; i++) DFS1(root->id, i);
        //从node开始向上跳,直到跳到某个点存在长度为len的子串
        //返回编号
        const auto jumpup = [&](int_t vtx, int_t len) {
            for (int_t i = SIZE2; i >= 0; i--) {
                auto node = nodes[vtx];
                if (len >= node->link->max + 1 && len <= node->max) break;
                if (jump[i][vtx] <= 0 || nodes[jump[i][vtx]]->link == nullptr) {
                    continue;
                }
                if (nodes[jump[i][vtx]]->max >= len) vtx = jump[i][vtx];
            }
            assert(len >= nodes[vtx]->link->max + 1 && len <= nodes[vtx]->max);
            return vtx;
        };
    
        //每个A和B对应的后缀树节点编号
        static int_t A[LARGE + 1], B[LARGE + 1];
        //每个AB对应的子串
        using pair_t = std::pair<int_t, int_t>;
        static pair_t As[LARGE + 1], Bs[LARGE + 1];
        //每个A B串对应的辅助节点的编号
        static int_t Aaux[LARGE + 1], Baux[LARGE + 1];
        struct SString {
            int_t type;
            int_t id;
            int_t len;
            int_t v1, v2;
        };
        //每个节点上挂的A和B的编号
        static std::vector<SString> strings[LARGE + 1];
        for (int_t i = 1; i <= id; i++) {
            strings[i].clear();
        }
        scanf("%d", &na);
        int_t minlen = len;
        for (int_t i = 1; i <= na; i++) {
            int_t left, right;
            scanf("%d%d", &left, &right);
            A[i] = jumpup(sufs[left]->id, right - left + 1);
            strings[A[i]].push_back(SString{1, i, right - left + 1});
            As[i] = pair_t(left, right);
            minlen = std::min(minlen, right - left + 1);
        }
        scanf("%d", &nb);
        for (int_t i = 1; i <= nb; i++) {
            int_t left, right;
            scanf("%d%d", &left, &right);
            B[i] = jumpup(sufs[left]->id, right - left + 1);
            strings[B[i]].push_back(SString{2, i, right - left + 1});
            Bs[i] = pair_t(left, right);
            // assert(right - left + 1 <= minlen);
        }
        //每个点的挂的子串按照长度从小到大排序,长度相同的,B串排在前面。
        int_t id0 = id;
        // 1号点没必要搞
        for (int_t i = 2; i <= id0; i++) {
            auto& curr = strings[i];
            std::sort(curr.begin(), curr.end(),
                      [](const SString& a, const SString& b) {
                          if (a.len == b.len)
                              return b.type < a.type;
                          else
                              return a.len < b.len;
                      });
    
            //新建一个点,用来代替出边
            int_t out = ++id;
            //把原来的出边全都改成从out出发
            for (int_t to : graph[i]) graph[out].push_back(to);
            graph[i].clear();
            graph[i].push_back(out);
            int_t last = -1;
            for (int_t j = 0; j < (int)curr.size(); j++) {
                //每个A串或B串建两个点,一个统计权值,另一个拿来往下走
                int_t v1 = ++id;
                int_t v2 = ++id;
                graph[i].push_back(v1);
                graph[v1].push_back(v2);
                curr[j].v1 = v1;
                curr[j].v2 = v2;
                //只有B类才可以不走支配边,直接沿着后缀链接树往下走
                // A类点必须走支配边
                if (curr[j].type == 1)
                    Aaux[curr[j].id] = v2;
                else if (curr[j].type == 2)
                    Baux[curr[j].id] = v1;
                if (curr[j].type == 1) {
                    val[v2] = curr[j].len;
                }
                // B类点才可以接着往子树走
                if (curr[j].type == 2) {
                    graph[v2].push_back(out);
                    continue;
                }
                if (last != -1) {
                    //每个A类点向比他长的连边,代表可以选这个也可以选比他长的
                    graph[last].push_back(v1);
                }
                last = v1;
            }
            for (int_t j = 0; j < curr.size(); j++) {
                if (curr[j].type == 2) {
                    if (j + 1 < curr.size()) {
                        //把B类点插到A类的中去
                        graph[curr[j].v1].push_back(curr[j + 1].v1);
                    }
                }
            }
        }
    
        scanf("%d", &m);
        for (int_t i = 1; i <= m; i++) {
            int_t a, b;
            scanf("%d%d", &a, &b);
            graph[Aaux[a]].push_back(Baux[b]);
        }
        auto result = DFS(root->id);
        memset(visited + 1, 0, sizeof(visited[0]) * id);
        memset(inStack + 1, 0, sizeof(inStack[0]) * id);
        memset(val + 1, 0, sizeof(val[0]) * id);
        used = 0;
        for (int_t i = 1; i <= id; i++) graph[i].clear();
        id = 0;
        return result;
    }
    int main() {
        int T;
        scanf("%d", &T);
        while (T--) {
            cout << process() << endl;
        }
        return 0;
    }

     

  • noi.ac 55-27 B (牛顿级数)

    题意

    $n,q\leq 10^9,k\leq 3000$.

    题解

    $$ \text{枚举满足条件的天数} \\ \left( 1+Q \right) ^n\sum_{0\le i\le n}{\left( \begin{array}{c} n\\ i\\ \end{array} \right) \frac{Q^i}{\left( 1+Q \right) ^n}\sum_{0\le j\le i}{j^k}} \\ =\sum_{0\le i\le n}{\left( \begin{array}{c} n\\ i\\ \end{array} \right) Q^i\sum_{0\le j\le i}{j^k}} \\ \text{设}f\left( n \right) =\sum_{0\le i\le n}{i^k} \\ \text{故原式}=\sum_{0\le i\le n}{\left( \begin{array}{c} n\\ i\\ \end{array} \right) Q^if\left( i \right)} \\ \text{将}f\left( i \right) \text{按照牛顿级数展开则有} \\ f\left( i \right) =\sum_{0\le j\le k+1}{\Delta ^jf\left( 0 \right) \left( \begin{array}{c} i\\ j\\ \end{array} \right)} \\ \text{原式}=\sum_{0\le i\le n}{\left( \begin{array}{c} n\\ i\\ \end{array} \right) Q^i\sum_{0\le j\le k+1}{\Delta ^jf\left( 0 \right) \left( \begin{array}{c} i\\ j\\ \end{array} \right)}} \\ =\sum_{0\le j\le k+1}{\Delta ^jf\left( 0 \right) \sum_{j\le i\le n}{\left( \begin{array}{c} n\\ i\\ \end{array} \right) \left( \begin{array}{c} i\\ j\\ \end{array} \right) Q^i}} \\ =\sum_{0\le j\le k+1}{\Delta ^jf\left( 0 \right) \sum_{j\le i\le n}{\frac{n!i!}{i!\left( n-i \right) !j!\left( i-j \right) !}Q^i}} \\ =\sum_{0\le j\le k+1}{\Delta ^jf\left( 0 \right) \left( \begin{array}{c} n\\ j\\ \end{array} \right) \sum_{j\le i\le n}{\frac{\left( n-j \right) !}{\left( n-i \right) !\left( i-j \right) !}Q^i}} \\ =\sum_{0\le j\le k+1}{\Delta ^jf\left( 0 \right) \left( \begin{array}{c} n\\ j\\ \end{array} \right) \sum_{j\le i\le n}{\left( \begin{array}{c} n-j\\ i-j\\ \end{array} \right) Q^i}} \\ =\sum_{0\le j\le k+1}{\Delta ^jf\left( 0 \right) \left( \begin{array}{c} n\\ j\\ \end{array} \right) Q^j\sum_{0\le i-j\le n-j}{\left( \begin{array}{c} n-j\\ i-j\\ \end{array} \right) Q^{i-j}}} \\ =\sum_{0\le j\le k+1}{\Delta ^jf\left( 0 \right) \left( \begin{array}{c} n\\ j\\ \end{array} \right) Q^j\sum_{0\le i\le n-j}{\left( \begin{array}{c} n-j\\ i\\ \end{array} \right) Q^i}} \\ =\sum_{0\le j\le k+1}{\Delta ^jf\left( 0 \right) \left( \begin{array}{c} n\\ j\\ \end{array} \right) Q^j\left( Q+1 \right) ^{n-j}} \\ =\sum_{0\le j\le k+1}{\frac{n^{\underline{j}}}{j!}\Delta ^jf\left( 0 \right) Q^j\left( Q+1 \right) ^{n-j}} \\ \text{由于}k\text{比较小所以暴力高阶差分即可。} \\ $$

    #include <iostream>
    
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    const int_t LARGE = 3010;
    const int_t mod = 1e9 + 7;
    int_t power(int_t base, int_t index) {
        int_t result = 1;
        while (index) {
            if (index & 1) result = result * base % mod;
            index >>= 1;
            base = base * base % mod;
        }
        return result;
    }
    
    int_t F[LARGE + 1], G[LARGE + 1];
    int_t n, k, q;
    int_t fact[LARGE + 1] = {1, 1}, factInv[LARGE + 1] = {1, 1};
    int_t C(int_t n, int_t m) {
    #ifdef DEBUG
        cout << "C " << n << " " << m << " = "
             << fact[n] * factInv[n - m] % mod * factInv[m] % mod << endl;
    #endif
        return fact[n] * factInv[n - m] % mod * factInv[m] % mod;
    }
    int main() {
        cin >> n >> k >> q;
    
        for (int_t i = 1; i <= LARGE; i++) {
            F[i] = (F[i - 1] + power(i, k)) % mod;
        }
        for (int_t i = 2; i <= LARGE; i++) {
            fact[i] = fact[i - 1] * i % mod;
            factInv[i] = (mod - mod / i) * factInv[mod % i] % mod;
        }
        for (int_t i = 2; i <= LARGE; i++)
            factInv[i] = factInv[i - 1] * factInv[i] % mod;
        if (k == 0) {
            cout << n * q % mod * power(q + 1, n - 1) % mod;
            return 0;
        }
        for (int_t i = 0; i <= k + 1; i++) {
            auto& x = G[i];
            for (int_t j = 0; j <= i; j++) {
                x = (x +
                     C(i, j) * (((i - j) % 2) ? (mod - 1) : 1) % mod * F[j] % mod) %
                    mod;
            }
        }
        int_t result = 0;
        int_t pow = 1l;
        for (int_t i = 0; i <= k + 1; i++) {
            if (i > n) break;
            result = (result + pow * factInv[i] % mod * G[i] % mod * power(q, i) %
                                   mod * power(q + 1, n - i) % mod) %
                     mod;
            pow = pow * (n - i) % mod;
        }
        cout << result << endl;
        return 0;
    }

     

  • UOJ269 【清华集训2016】如何优雅地求和

    $$ \text{定义差分算子}\Delta f\left( x \right) =f\left( x+1 \right) -f\left( x \right) \\ \Delta ^kf\left( x \right) =\Delta ^{k-1}f\left( x+1 \right) -\Delta ^{k-1}f\left( x \right) \\ \text{定义算子}E,Ef\left( x \right) =Ef\left( x+1 \right) \\ \text{故}\Delta ^kf\left( x \right) =\Delta ^{k-1}Ef\left( x \right) -\Delta ^{k-1}f\left( x \right) \\ \text{除掉}\Delta ^{k-1}f\left( x \right) \text{可得} \\ \Delta =E-1 \\ \text{故}\Delta ^k=\left( E-1 \right) ^k=\sum_{0\le i\le k}{\left( \begin{array}{c} k\\ i\\ \end{array} \right) E^i\left( -1 \right) ^{k-i}} \\ \text{故}\Delta ^kf\left( x \right) =\sum_{0\le i\le k}{\left( \begin{array}{c} k\\ i\\ \end{array} \right) \left( -1 \right) ^{k-i}E^if\left( x \right)} \\ =\sum_{0\le i\le k}{\left( \begin{array}{c} k\\ i\\ \end{array} \right) \left( -1 \right) ^{k-i}f\left( x+i \right)} \\ =k!\sum_{0\le i\le k}{\frac{f\left( x+i \right)}{i!}\times \frac{\left( -1 \right) ^{k-i}}{\left( k-i \right) !}} \\ \text{所以}x\text{固定时可以通过多项式乘法在}O\left( k\log k \right) \text{的时间复杂度内计算}f\left( x \right) \text{的0到}k\text{阶差分。} \\ \text{对于}k\text{次多项式}F\left( x \right) ,\text{定义牛顿级数} \\ F\left( x \right) =\sum_{0\le i\le k}{\Delta ^iF\left( 0 \right) \left( \begin{array}{c} x\\ i\\ \end{array} \right)}=\sum_{0\le i\le k}{\Delta ^iF\left( 0 \right) \frac{x^{\underline{i}}}{i!}} \\ \text{故题目的式子} \\ Q\left( f,n,x \right) =\sum_{0\le i\le n}{\begin{array}{c} f\left( i \right) \left( \begin{array}{c} n\\ i\\ \end{array} \right) x^i\left( 1-x \right) ^{n-i}\\ \end{array}} \\ =\sum_{0\le i\le n}{\begin{array}{c} \sum_{0\le j\le i}{\Delta ^jf\left( 0 \right) \left( \begin{array}{c} i\\ j\\ \end{array} \right)}\left( \begin{array}{c} n\\ i\\ \end{array} \right) x^i\left( 1-x \right) ^{n-i}\\ \end{array}} \\ =\sum_{0\le j\le n}{\Delta ^jf\left( 0 \right) \sum_{j\le i\le n}{\left( \begin{array}{c} n\\ i\\ \end{array} \right) \left( \begin{array}{c} i\\ j\\ \end{array} \right) x^i\left( 1-x \right) ^{n-i}}} \\ =\sum_{0\le j\le n}{\Delta ^jf\left( 0 \right) \sum_{j\le i\le n}{\frac{n!i!}{i!\left( n-i \right) !j!\left( n-j \right) !}x^i\left( 1-x \right) ^{n-i}}} \\ =\sum_{0\le j\le n}{\Delta ^jf\left( 0 \right) \sum_{j\le i\le n}{\frac{n!}{\left( n-i \right) !j!\left( i-j \right) !}x^i\left( 1-x \right) ^{n-i}}} \\ =\sum_{0\le j\le n}{\Delta ^jf\left( 0 \right) \frac{n!}{j!\left( n-j \right) !}\sum_{j\le i\le n}{\frac{\left( n-j \right) !}{\left( n-i \right) !\left( i-j \right) !}x^i\left( 1-x \right) ^{n-i}}} \\ =\sum_{0\le j\le n}{\Delta ^jf\left( 0 \right) \left( \begin{array}{c} n\\ j\\ \end{array} \right) \sum_{j\le i\le n}{\left( \begin{array}{c} n-j\\ i-j\\ \end{array} \right) x^i\left( 1-x \right) ^{n-i}}} \\ =\sum_{0\le j\le n}{\Delta ^jf\left( 0 \right) \left( \begin{array}{c} n\\ j\\ \end{array} \right) \sum_{j\le i+j\le n}{\left( \begin{array}{c} n-j\\ i\\ \end{array} \right) x^{i+j}\left( 1-x \right) ^{n-i-j}}} \\ =\sum_{0\le j\le n}{\Delta ^jf\left( 0 \right) \left( \begin{array}{c} n\\ j\\ \end{array} \right) x^j\sum_{0\le i\le n-j}{\left( \begin{array}{c} n-j\\ i\\ \end{array} \right) x^i\left( 1-x \right) ^{n-i-j}}} \\ =\sum_{0\le j\le n}{\Delta ^jf\left( 0 \right) \left( \begin{array}{c} n\\ j\\ \end{array} \right) x^j\left( x+\left( 1-x \right) \right) ^{n-j}} \\ =\sum_{0\le j\le n}{\Delta ^jf\left( 0 \right) \left( \begin{array}{c} n\\ j\\ \end{array} \right) x^j} \\ =\sum_{0\le j\le n}{\Delta ^jf\left( 0 \right) x^j\frac{n^{\underline{j}}}{j!}} \\ \text{对于}m\text{次多项式}f\left( x \right) ,\text{显然}\Delta ^kf\left( x \right) \text{在}k\text{超过}m\text{时为}0. \\ \text{在已知点值的情况下,我们可以使用上面的结论在}O\left( k\log k \right) \text{的时间内计算出多项式}f\left( x \right) \text{在}x=\text{0处的0到}k\text{阶差分。} \\ \text{复杂度}O\left( m\log m \right) $$

    #include <assert.h>
    #include <algorithm>
    #include <iostream>
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    
    const int_t mod = 998244353;
    const int_t g = 3;
    
    int_t power(int_t base, int_t index) {
        int_t result = 1;
        const auto phi = mod - 1;
        if (index < 0) index = (index % phi + phi) % phi;
        while (index) {
            if (index & 1) result = result * base % mod;
            index >>= 1;
            base = base * base % mod;
        }
        return result;
    }
    void transform(int_t* A, const int_t size2, int_t arg);
    int_t upper2n(int_t x) {
        int_t result = 0;
        while ((1 << result) < x) result++;
        return result;
    }
    
    const int_t LARGE = 1 << 16;
    int_t fact[LARGE + 1], factInv[LARGE + 1];
    int main() {
        fact[0] = factInv[0] = fact[1] = factInv[1] = 1;
        for (int_t i = 2; i <= LARGE; i++) {
            fact[i] = fact[i - 1] * i % mod;
            factInv[i] = (mod - mod / i) * factInv[mod % i] % mod;
        }
        for (int_t i = 2; i <= LARGE; i++)
            factInv[i] = factInv[i - 1] * factInv[i] % mod;
        int_t n, m, x;
        scanf("%lld%lld%lld", &n, &m, &x);
        static int_t F[LARGE + 1], G[LARGE + 1];
        for (int_t i = 0; i <= m; i++) scanf("%lld", &F[i]);
        for (int_t i = 0; i <= m; i++) {
            F[i] = F[i] * factInv[i] % mod;
            G[i] = power(mod - 1, i) * factInv[i] % mod;
        }
        const int_t size2 = upper2n(2 * m + 1);
        transform(F, size2, 1);
        transform(G, size2, 1);
        for (int_t i = 0; i < (1 << size2); i++) {
            F[i] = F[i] * G[i] % mod;
        }
        transform(F, size2, -1);
        const int_t inv = power(1 << size2, -1);
        for (int_t i = 0; i <= m; i++) {
            //F[i]是f(0)的i阶差分值
            F[i] = F[i] * inv % mod * fact[i] % mod;
        }
        int_t result = 0;
        int_t pown = 1, pow = 1;
        for (int_t i = 0; i <= m; i++) {
            result =
                (result + F[i] * pow % mod * pown % mod * factInv[i] % mod) % mod;
            pown = pown * (n - i) % mod;
            pow = pow * x % mod;
        }
        cout << result << endl;
        return 0;
    }
    
    void transform(int_t* A, const int_t size2, int_t arg) {
        const auto rev = [=](int_t x) {
            int_t result = 0;
            for (int_t i = 1; i < size2; i++) {
                result |= (x & 1);
                result <<= 1;
                x >>= 1;
            }
            return result | (x & 1);
        };
        for (int_t i = 0; i < (1 << size2); i++) {
            int_t x = rev(i);
            if (x > i) std::swap(A[i], A[x]);
        }
        int_t length = 1 << size2;
        for (int_t i = 2; i <= length; i *= 2) {
            int_t mr = power(g, arg * (mod - 1) / i);
            assert(power(mr, i) == 1);
            for (int_t j = 0; j < length; j += i) {
                int_t curr = 1;
                for (int_t k = 0; k < i / 2; k++) {
                    int_t u = A[j + k];
                    int_t t = A[j + k + i / 2] * curr % mod;
                    A[j + k] = (u + t) % mod;
                    A[j + k + i / 2] = (u - t + mod) % mod;
                    //更新curr的位置错了
                    curr = curr * mr % mod;
                }
            }
        }
    }

     

  • CF932E Team work

    $$ \sum_{1\le i\le n}{\left( \begin{array}{c} n\\ i\\ \end{array} \right) i^k}=\sum_{1\le i\le n}{\begin{array}{c} \left( \begin{array}{c} n\\ i\\ \end{array} \right) \sum_{0\le j\le k}{\left\{ \begin{array}{c} k\\ j\\ \end{array} \right\} i^{\underline{j}}}\\ \end{array}} \\ =\sum_{1\le i\le n}{\begin{array}{c} \left( \begin{array}{c} n\\ i\\ \end{array} \right) \sum_{0\le j\le k}{\left\{ \begin{array}{c} k\\ j\\ \end{array} \right\} j!\frac{i!}{\left( i-j \right) !j!}}\\ \end{array}} \\ =\sum_{1\le i\le n}{\left( \begin{array}{c} n\\ i\\ \end{array} \right) \sum_{0\le j\le k}{\left\{ \begin{array}{c} k\\ j\\ \end{array} \right\} j!\left( \begin{array}{c} i\\ j\\ \end{array} \right)}} \\ =\sum_{0\le j\le k}{\left\{ \begin{array}{c} k\\ j\\ \end{array} \right\} j!\sum_{1\le i\le n}{\left( \begin{array}{c} n\\ i\\ \end{array} \right)}\left( \begin{array}{c} i\\ j\\ \end{array} \right)} \\ =\sum_{0\le j\le k}{\left\{ \begin{array}{c} k\\ j\\ \end{array} \right\} j!\sum_{1\le i\le n}{\frac{n!i!}{i!\left( n-i \right) !j!\left( i-j \right) !}}} \\ =\sum_{0\le j\le k}{\begin{array}{c} \left\{ \begin{array}{c} k\\ j\\ \end{array} \right\} j!\sum_{1\le i\le n}{\frac{n!}{\left( n-i \right) !j!\left( i-j \right) !}}\\ \end{array}} \\ =\sum_{0\le j\le k}{\left\{ \begin{array}{c} k\\ j\\ \end{array} \right\} \frac{n!}{\left( n-j \right) !}\sum_{1\le i\le n}{\frac{\left( n-j \right) !}{\left( n-i \right) !\left( i-j \right) !}}} \\ =\sum_{0\le j\le k}{\left\{ \begin{array}{c} k\\ j\\ \end{array} \right\} \frac{n!}{\left( n-j \right) !}\sum_{1\le i\le n,n-i\le n-j}{\left( \begin{array}{c} n-j\\ n-i\\ \end{array} \right)}} \\ =\sum_{0\le j\le k}{\left\{ \begin{array}{c} k\\ j\\ \end{array} \right\} \frac{n!}{\left( n-j \right) !}\sum_{j\le i\le n}{\left( \begin{array}{c} n-j\\ i-j\\ \end{array} \right)}} \\ =\sum_{0\le j\le k}{\left\{ \begin{array}{c} k\\ j\\ \end{array} \right\} \frac{n!}{\left( n-j \right) !}\sum_{j\le i+j\le n}{\left( \begin{array}{c} n-j\\ i\\ \end{array} \right)}} \\ =\sum_{0\le j\le k}{\left\{ \begin{array}{c} k\\ j\\ \end{array} \right\} \frac{n!}{\left( n-j \right) !}\sum_{0\le i\le n-j}{\left( \begin{array}{c} n-j\\ i\\ \end{array} \right)}} \\ =n!\sum_{0\le j\le k}{\frac{\left\{ \begin{array}{c} k\\ j\\ \end{array} \right\}}{\left( n-j \right) !}2^{n-j}} \\ =\sum_{0\le j\le k}{\left\{ \begin{array}{c} k\\ j\\ \end{array} \right\} n^{\underline{j}}2^{n-j}} \\ \text{下降幂直接暴力计算即可。} \\ $$

    #include <iostream>
    
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    
    const int_t mod = 1e9 + 7;
    const int_t LARGE = 5000;
    int S2[LARGE + 1][LARGE + 1];
    int_t power(int_t base, int_t index) {
        int_t result = 1;
        while (index) {
            if (index & 1) result = result * base % mod;
            index >>= 1;
            base = base * base % mod;
        }
        return result;
    }
    int main() {
        S2[0][0] = 1;
        for (int i = 1; i <= LARGE; i++) {
            for (int j = 1; j <= i; j++) {
                S2[i][j] = ((int_t)S2[i - 1][j] * j % mod + S2[i - 1][j - 1]) % mod;
            }
        }
        int_t n, k;
        cin >> n >> k;
        int_t result = 0;
        int_t prod = 1;
        int_t pow = ::power(2, n);
        const int_t inv = power(2, mod - 2);
        for (int_t i = 0; i <= k; i++) {
            if (i > n) break;
            result = (result + prod * S2[k][i] % mod * pow % mod) % mod;
            pow = pow * inv % mod;
            prod = prod * (n - i) % mod;
    #ifdef DEBUG
    
            cout << "S2 " << k << " " << i << " = " << S2[k][i] << endl;
    #endif
        }
        cout << result << endl;
        return 0;
    }

     

  • CF1153D Serval and Rooted Tree

    又是一道送分题…没救了

    可能会考虑二分答案?

    二分一个答案x,把大于等于x的数字设置为1,其他设置为0,然后设$f(x)$表示使得x号点为1,那么x的子树内的叶子节点至少需要几个1.

    然后发现后面这个check和前面二分的x没有任何关系。

    然后直接跑出来f(1),我们就知道了要让根节点最大,我们至少需要几个1.

    然后把f(1)从大到小钦定给k,k-1,k-2…1。最后一个被钦定的元素就是答案。

    #include <iostream>
    #include <vector>
    using int_t = int;
    using std::cin;
    using std::cout;
    using std::endl;
    
    const int_t LARGE = 1e6;
    int_t opt[LARGE + 1];
    std::vector<int_t> graph[LARGE + 1];
    bool isLeaf[LARGE + 1];
    int_t n;
    int_t DFS(int_t vtx) {
        if (isLeaf[vtx]) return 1;
        if (opt[vtx] == 0) {
            int_t result = 0;
            for (int_t to : graph[vtx]) result += DFS(to);
            return result;
        } else {
            int_t result = 0x7fffffff;
            for (int_t to : graph[vtx]) result = std::min(result, DFS(to));
            return result;
        }
    }
    int main() {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &opt[i]);
        }
        for (int i = 2; i <= n; i++) {
            int parent;
            scanf("%d", &parent);
            graph[parent].push_back(i);
        }
        int_t count = 0;
        for (int i = 1; i <= n; i++)
            if (graph[i].empty()) {
                isLeaf[i] = true;
                count++;
            }
        cout << count + 1 - DFS(1) << endl;
        return 0;
    }

     

  • CF1153C Serval and Parenthesis Sequence

    普及题我都不会做…我还能说什么…

    首先原题等价于删掉首尾后,剩下的部分是一个合法的括号序列。

    所以先判掉首尾不合法。

    然后从第2个元素扫到第n-2个元素,统计出空白的位置和前缀和(记左括号为1,右括号为-1)

    如果前缀和为负,那么就先安排上一定数量的左括号。

    如果前缀和为正,那么就先安排上一定数量的右括号。

    如果前缀和为0,那么左右括号安排相等的数量。

    然后我们知道了左右括号各需要多少个,从头开始扫,遇到空位优先放左括号,放完了再放右括号。

    最后检查一遍是否合法即可。

    #include <assert.h>
    #include <cstdlib>
    #include <iostream>
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    
    const int_t LARGE = 1e6;
    char str[LARGE + 1];
    int_t val[LARGE + 1];
    int_t n;
    void fail() {
        cout << ":(";
        exit(0);
    }
    int main() {
        cin >> n;
        scanf("%s", str + 1);
        int_t count = 0;
        int_t prefix = 0;
        for (int_t i = 2; i <= n - 1; i++) {
            if (str[i] == '(')
                val[i] = 1;
            else if (str[i] == ')')
                val[i] = -1;
            else
                count++;
            prefix += val[i];
        }
        int_t a = 0, b = 0;
        if (prefix < 0)
            a += -prefix;
        else
            b += prefix;
        int_t diff = count - a - b;
    
        if (diff < 0) {
            fail();
    
        } else if (diff % 2)
            fail();
        else {
            a += diff / 2;
            b += diff / 2;
        }
    #ifdef DEBUG
        cout << "a = " << a << " b = " << b << endl;
    #endif
        if (str[1] == ')') fail();
        if (str[n] == '(') fail();
        for (int_t i = 2; i <= n - 1; i++) {
            if (val[i] == 0) {
                if (a > 0) {
                    val[i] = 1;
                    a--;
                } else if (b > 0) {
                    val[i] = -1;
                    b--;
                }
            }
        }
        if (a || b) fail();
        val[1] = 1;
        val[n] = -1;
        prefix = 0;
        for (int_t i = 1; i < n; i++) {
            prefix += val[i];
            if (prefix == 0) fail();
        }
        prefix += val[n];
        if (prefix != 0) fail();
        for (int_t i = 1; i <= n; i++) {
            if (val[i] == 1)
                putchar('(');
            else if (val[i] == -1)
                putchar(')');
            else
                assert(false);
        }
        return 0;
    }

     

  • 12省联考2019 异或粽子

    这么简单的题考场上对一个得分等同n^2的假做法调了4.5h,没救了。

    直接考虑维护出前k大的值。

    怎么维护?

    首先把和每一个点异或出来最大的值扔进堆里,使用(val,pos,rank)表示val是点pos与其他点异或起来第rank大的值。

    每次从堆里拿出来一个元素(val,pos,rank),然后val加入答案,然后把(valx,pos,rank+1)扔进堆里。

    查询与一个数异或起来第k大的值可以通过01Trie维护。

    然后注意一个事情,一个区间会被算两遍。

    比如最大的区间[a,b] 会被a统计到,又会被b统计到。

    所以把k乘2,最后答案除2即可。

    二轮考场千万别心态爆炸了!!好好做题!!正确的策略!!

     

    // luogu-judger-enable-o2
    // luogu-judger-enable-o2
    #include <ext/pb_ds/priority_queue.hpp>
    #include <iostream>
    #include <queue>
    #include <utility>
    #include <inttypes.h>
    using int_t = int;
    using std::cin;
    using std::cout;
    using std::endl;
    
    using pair_t = std::pair<uint32_t, int_t>;
    
    const int BITS = 32;
    struct Node {
        Node* chds[2] = {nullptr, nullptr};
        int count = 0;
        int sum = 0;
        void maintain() {
            sum = count;
            if (chds[0]) sum += chds[0]->sum;
            if (chds[1]) sum += chds[1]->sum;
        }
        int leftSize() {
            if (chds[0]) return chds[0]->sum;
            return 0;
        }
        int rightSize() {
            if (chds[1]) return chds[1]->sum;
            return 0;
        }
    };
    uint32_t bitAt(uint32_t x, int_t bit) { return (x & (((uint32_t)1) << bit)) != 0; }
    
    Node* insert(Node* root, uint32_t val, int_t bit = BITS - 1) {
        if (root == nullptr) root = new Node;
        if (bit == -1) {
            root->count += 1;
            root->maintain();
            return root;
        }
        int_t bitx = bitAt(val, bit);
        root->chds[bitx] = insert(root->chds[bitx], val, bit - 1);
        root->maintain();
        return root;
    }
    uint32_t kMaxXor(Node* root, uint32_t val, int_t k) {
        uint32_t result = 0;
        for (int_t i = BITS - 1; i >= 0; i--) {
            int_t bit = bitAt(val, i);
            if (bit == 0) {
                if (k <= root->rightSize()) {
                    root = root->chds[1];
                    result |= (1ll << i);
                } else {
                    k -= root->rightSize();
                    root = root->chds[0];
                }
            } else {
                if (k <= root->leftSize()) {
                    root = root->chds[0];
                    result |= (1ll << i);
                } else {
                    k -= root->leftSize();
                    root = root->chds[1];
                }
            }
        }
        return result;
    }
    int main() {
        // std::priority_queue<pair_t, std::vector<pair_t>> heap;
        __gnu_pbds::priority_queue<pair_t,std::less<pair_t>, __gnu_pbds::pairing_heap_tag> heap;
        Node* root = new Node;
        int_t n, k;
        scanf("%d%d", &n, &k);
        const int_t LARGE = 5e5;
        static uint32_t vals[LARGE + 1];
        static int_t curr[LARGE + 1];
        static uint32_t prefix[LARGE + 1];
        root = insert(root, 0);
        for (int_t i = 1; i <= n; i++) {
            scanf("%u", &vals[i]);
            prefix[i] = prefix[i - 1] ^ vals[i];
            root = insert(root, prefix[i]);
            // root = insert(root, vals[i]);
            curr[i] = 1;
        }
        curr[0] = 1;
        for (int_t i = 1; i <= n; i++) {
            heap.push(pair_t{kMaxXor(root, prefix[i], 1), i});
        }
        heap.push(pair_t{kMaxXor(root, 0, 1), 0});
        int64_t result = 0;
        for (int_t i = 1; i <= k * 2; i++) {
            auto top = heap.top();
            heap.pop();
            result += top.first;
            curr[top.second] += 1;
            heap.push(pair_t{kMaxXor(root, prefix[top.second], curr[top.second]),
                             top.second});
        }
        cout << result / 2 << endl;
        return 0;
    }

     

  • CF600E Lomsat gelral

    题意:

    给定一棵以1号点为根的有根树,点有点权,请输出每棵子树中出现次数最多的点权的和。

    直接考虑树上启发式合并。

    首先划分出轻重链。

    维护一个全局map,存储每个点权的点的出现次数。

    然后DFS处理,对于一个点vtx,首先递归它的轻子节点,递归完成后清空map。

    然后递归重子节点,递归完成后不清空map(直接把重子节点的答案拖过来)。

    然后直接暴力DFS非重子节点的所有子树,把这些子树里面的所有点加入map,更新答案即可。

    然后就得到了vtx的答案。

    然后根据情况,如果vtx是其父节点的轻子节点,那么再DFS一遍vtx这棵子树(包括重链),撤销影响,如果vtx是其父节点的重子节点,那么直接返回。

    复杂度分析?

    考虑一个点被访问的次数。

    如果一个点v在某次暴力DFS轻边过程中被访问,那么这种访问只有logn次(一个点到树根最多经过logn调轻边)。

    如果一个点v在其他情况下被访问,那么显然只有一次。

    不要写了假的树剖!!

    #include <algorithm>
    #include <iostream>
    #include <map>
    #include <unordered_map>
    #include <vector>
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    
    const int_t LARGE = 5e5;
    int_t maxChd[LARGE + 1];
    std::vector<int_t> graph[LARGE + 1];
    int_t val[LARGE + 1], size[LARGE + 1];
    
    //划分轻重链
    void DFS1(int_t vtx, int_t from = -1) {
        size[vtx] = 1;
        int_t& maxChd = ::maxChd[vtx];
        maxChd = 0;
        for (int_t to : graph[vtx]) {
            if (to != from) {
                DFS1(to, vtx);
                //不要写了假的树剖!!
                size[vtx] += size[to];
                if (size[to] > size[maxChd]) {
                    maxChd = to;
                }
            }
        }
    }
    int_t results[LARGE + 1];
    //某个权值的点的出现次数
    std::map<int_t, int_t> map;
    int_t result, max;
    //暴力轻子节点对应的子树
    void DFS2(int_t vtx, int_t from, int_t forb, int_t arg) {
        //加入当前点
        map[val[vtx]] += arg;
        if (map[val[vtx]] == 0) map.erase(val[vtx]);
        if (map[val[vtx]] > max) {
            max = map[val[vtx]];
            result = val[vtx];
        } else if (map[val[vtx]] == max) {
            result += val[vtx];
        }
        //递归下去暴力
        for (int_t to : graph[vtx]) {
            if (to != from && to != forb) {
                DFS2(to, vtx, forb, arg);
            }
        }
    }
    //主DFS过程
    void DFS3(int_t vtx, int_t from = -1, bool flag = false) {
        // DFS处理轻子节点并删掉其影响
        for (int_t to : graph[vtx]) {
            if (to != from && to != maxChd[vtx]) DFS3(to, vtx, true);
        }
        //处理重子节点后答案直接拖过来
        if (maxChd[vtx]) {
            DFS3(maxChd[vtx], vtx, false);
        }
        //暴力除了重子节点之外的节点
        DFS2(vtx, from, maxChd[vtx], 1);
        //记录答案
        results[vtx] = result;
        //删除整棵树对答案的影响
        if (flag) {
            DFS2(vtx, from, 0, -1);
            result = max = 0;
        }
    }
    template <class T>
    void read(T& x) {
        x = 0;
        auto chr = getchar();
        while (isdigit(chr) == false) chr = getchar();
        while (isdigit(chr)) {
            x = x * 10 + chr - '0';
            chr = getchar();
        }
    }
    template <int_t base = 10, class T>
    void write(T x) {
        if (x >= base) write<base>(x / base);
        putchar(x % base + '0');
    }
    
    int n;
    int main() {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) read(val[i]);
        for (int i = 1; i <= n - 1; i++) {
            int from, to;
            read(from);
            read(to);
            graph[from].push_back(to);
            graph[to].push_back(from);
            // int x;
            // read(x);
        }
        DFS1(1);
        DFS3(1);
        for (int i = 1; i <= n; i++) {
            write(results[i]);
            printf(" ");
        }
        cout << endl;
        return 0;
    }