计蒜客2018蓝桥A组模拟赛(一)青出于蓝胜于蓝(dfs序+线段树)

题目:

武当派一共有 n 人,门派内 n 人按照武功高低进行排名,武功最高的人排名第 1,次高的人排名第 2,… 武功最低的人排名第 n。现在我们用武功的排名来给每个人标号,除了祖师爷,每个人都有一个师父,每个人可能有多个徒弟。

我们知道,武当派人才辈出,连祖师爷的武功都只能排行到 p。也就是说徒弟的武功是可能超过师父的,所谓的青出于蓝胜于蓝。

请你帮忙计算每个人的所有子弟(包括徒弟的徒弟,徒弟的徒弟的徒弟….)中,有多少人的武功超过了他自己。

1 <= n <= 100000

分析:

这道题就是:给一棵带权树,求每个节点的子树上有多少个权值比它小的点。

显然,对每个节点遍历子树肯定会超时。

我们对这棵树dfs一次,按照dfs的顺序给每个点编号。这样,按照遍历的顺序我们就得到了一个线性序列。更重要的是,点 i 的子树在线性序列中就紧跟点 i 后面。所以我们如果知道它子树这一段的起点 l[i] 和终点 r[i],就可以用线段树解决。很显然,dfs的顺序得到的编号就是 l[i]。在dfs的时候,某个点的子树遍历完了的时候,再记录一下 r[i]

这样,在dfs得到的线性序列中,点 i 的子树就是 l[i]r[i]

我们从小排名到大排名更新和查询,对第 i 名的人,比他名次小的人数 l[i]r[i] 的人数和。因为我们是按照从小到达查询更新,所以比 i 排名大的点还没更新进树里。

代码:

#include <bits/stdc++.h>
using namespace std;
#define ms(a,b) memset(a,b,sizeof(a))
#define lson rt*2,l,(l+r)/2
#define rson rt*2+1,(l+r)/2+1,r
typedef unsigned long long ull;
typedef long long ll;
const int MAXN = 100005;
const double EPS = 1e-8;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
int tree[MAXN << 2], n, p, l[MAXN], r[MAXN], times;
vector<int>G[MAXN];

void init() {
    times = 0;
    ms(tree, 0);
}

void pushup(int rt) {
    tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
}

void dfs(int u, int fa) {
    l[u] = ++times;
    for (int i = 0; i < (int)G[u].size(); i++) {
        int v = G[u][i];
        if (v != fa) {
            dfs(v, u);
        }
    }
    r[u] = times;
}

int query(int L, int R, int rt, int l, int r) {
    if (L <= l && R >= r) {
        return tree[rt];
    }
    int ret = 0;
    if (L <= (l + r) / 2)   ret += query(L, R, lson);
    if (R > (l + r) / 2)    ret +=query(L,R,rson);
    return ret;
}

void update(int k, int rt, int l, int r) {
    if (l == r) {
        tree[rt] = 1;
        return;
    }
    if (k <= (l + r) / 2)   update(k, lson);
    else    update(k, rson);
    pushup(rt);
}

int main() {
    ios::sync_with_stdio(false);
    cin >> n >> p;
    init();
    for (int i = 0; i < n - 1; i++) {
        int u, v;
        cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(p, -1);
    // for(int i=1;i<=n;i++){
    //  cout <<i << " " << l[i] << " " << r[i] << endl;
    // }
    for (int i = 1; i <= n; i++) {
        cout << query(l[i], r[i], 1, 1, n) << " \n"[i == n];
        update(l[i], 1, 1, n);
    }

    return 0;
}
阅读更多
上一篇Codeforces915E Physical Education Lessons
下一篇计蒜客2018蓝桥A模拟赛(一) 蒜头君王国(概率计算)
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭