题目:
武当派一共有 n 人,门派内 n 人按照武功高低进行排名,武功最高的人排名第 1,次高的人排名第 2,… 武功最低的人排名第 n。现在我们用武功的排名来给每个人标号,除了祖师爷,每个人都有一个师父,每个人可能有多个徒弟。
我们知道,武当派人才辈出,连祖师爷的武功都只能排行到 p。也就是说徒弟的武功是可能超过师父的,所谓的青出于蓝胜于蓝。
请你帮忙计算每个人的所有子弟(包括徒弟的徒弟,徒弟的徒弟的徒弟….)中,有多少人的武功超过了他自己。
1 <= n <= 100000
分析:
这道题就是:给一棵带权树,求每个节点的子树上有多少个权值比它小的点。
显然,对每个节点遍历子树肯定会超时。
我们对这棵树dfs一次,按照dfs的顺序给每个点编号。这样,按照遍历的顺序我们就得到了一个线性序列。更重要的是,点 ii 的子树在线性序列中就紧跟点 后面。所以我们如果知道它子树这一段的起点 l[i]l[i] 和终点 r[i]r[i],就可以用线段树解决。很显然,dfs的顺序得到的编号就是 l[i]l[i]。在dfs的时候,某个点的子树遍历完了的时候,再记录一下 r[i]r[i]
这样,在dfs得到的线性序列中,点 ii 的子树就是 到 r[i]r[i] 。
我们从小排名到大排名更新和查询,对第 ii 名的人,比他名次小的人数 到 r[i]r[i] 的人数和。因为我们是按照从小到达查询更新,所以比 ii 排名大的点还没更新进树里。
代码:
#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;
}

本文介绍了一种解决特定树形结构问题的算法方案——通过深度优先搜索(DFS)将树形结构转化为线性序列,并利用线段树进行区间查询与更新,以高效计算每个节点子树中满足条件的节点数量。
青出于蓝胜于蓝(dfs序+线段树)&spm=1001.2101.3001.5002&articleId=79968153&d=1&t=3&u=1e07f8ac075b4688a52ab8e9c75a10cf)
941

被折叠的 条评论
为什么被折叠?



