zoukankan      html  css  js  c++  java
  • Codeforces #447 Div2 D

    #447 Div2 D

    题意

    给一棵完全二叉树,每条边有权值为两点间的距离,每次询问 (x, h) ,从结点 (x) 出发到某一结点的最短路的距离 (d) 如果小于 (h) ,则答案加上 (h - d) ,考虑所有结点并输出答案。

    分析

    通过建树过程可以发现这是一棵完全二叉树,也就是说树很矮。
    可以预处理这棵树,对于每一个结点,我们可以计算出以这个结点为根结点的子树中的所有结点到当前子树的根结点的距离,从根结点向下 DFS 即可,然后自下而上合并,类似归并排序合并的过程。再预处理下前缀和,这样就很容易求得子树中有多少结点到根结点的距离小于 (h) ,向上走 (log(n)) 次一定可以到根结点。

    完全二叉树 很矮!祖先结点很少!很多情况都可以遍历(暴力)祖先结点!

    code

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e6 + 10;
    int f, s, mx;
    int n, m, a[N], l[N], r[N];
    vector<int> G[N];
    vector<ll> S[N];
    void mergeUp(int rt, int cl, int cr) {
        int i = 0, j = 0;
        while(i < cl || j < cr) {
            if(i == cl) G[rt].push_back(r[j++]);
            else if(j == cr) G[rt].push_back(l[i++]);
            else {
                if(l[i] < r[j]) G[rt].push_back(l[i++]);
                else G[rt].push_back(r[j++]);
            }
        }
        ll sum = 0;
        for(int i = 0; i < G[rt].size(); i++) {
            sum += G[rt][i];
            S[rt].push_back(sum);
        }
    }
    void build(int rt) {
        if(rt >= s - mx + 1) return;
        build(rt * 2);
        build(rt * 2 + 1);
        int cl = 0, cr = 0;
        if(rt * 2 <= n) for(int i = 0; i < G[rt * 2].size(); i++) {
            l[cl++] = G[rt * 2][i] + a[rt * 2];
        }
        if(rt * 2 + 1 <= n) for(int i = 0; i < G[rt * 2 + 1].size(); i++) {
            r[cr++] = G[rt * 2 + 1][i] + a[rt * 2 + 1];
        }
        if(cl + cr > 0) mergeUp(rt, cl, cr);
    }
    int main() {
        scanf("%d%d", &n, &m);
        s = 1, mx = 1, f = 0;
        while(s < n) {
            mx *= 2;
            s += mx;
            f++;
        }
        for(int i = 1; i < n; i++) {
            int x;
            scanf("%d", &x);
            a[i + 1] = x;
            G[i].push_back(0);
        }
        G[n].push_back(0);
        build(1);
        while(m--) {
            int now, h, pre = -1;
            scanf("%d%d", &now, &h);
            ll ans = h;
            while(now) {
                if(now != 1 && a[now] < h) ans += h - a[now];
                if(pre == -1) {
                    int pos = lower_bound(G[now].begin(), G[now].end(), h) - G[now].begin() - 1;
                    if(pos > 0) ans += 1LL * pos * h - S[now][pos];
                } else {
                    if(now * 2 == pre && now * 2 + 1 <= n) {
                        int nxt = now * 2 + 1;
                        int pos = lower_bound(G[nxt].begin(), G[nxt].end(), h - a[nxt]) - G[nxt].begin() - 1;
                        if(pos > 0) ans += 1LL * pos * (h - a[nxt]) - S[nxt][pos];
                        if(a[nxt] < h) ans += h - a[nxt];
                    } else if(now * 2 + 1 == pre && now * 2 <= n) {
                        int nxt = now * 2;
                        int pos = lower_bound(G[nxt].begin(), G[nxt].end(), h - a[nxt]) - G[nxt].begin() - 1;
                        if(pos > 0) ans += 1LL * pos * (h - a[nxt]) - S[nxt][pos];
                        if(a[nxt] < h) ans += h - a[nxt];
                    }
                }
                h -= a[now];
                pre = now;
                now /= 2;
            }
            cout << ans << endl;
        }
        return 0;
    }
    
  • 相关阅读:
    比较Maven和Ant
    解决浏览器缓存
    Servlet--HttpServletResponse的2个操作流的方法
    Servlet--j2e中文乱码解决
    java乱码详解(java中byte与char的转换)
    linux中操作java进程
    Servlet--超链接,表单提交,重定向,转发4种情况的路径
    物理路径,相对路径,绝对路径以及根目录
    Servlet--转发和重定向
    Servlet--传参和接参
  • 原文地址:https://www.cnblogs.com/ftae/p/7875241.html
Copyright © 2011-2022 走看看