洛谷P3605 [USACO17JAN]Promotion Counting P(离散化 + 树状数组 或 线段树合并)

题目链接

题意:给你一棵以 1 1 1为根节点的树和每个点的点权,问在每个子树中,有多少个子节点的点权小于根节点。

题解
首先对值进行离散后,之后有两种方法可以解决。

  • 树状数组
    求有几个比一个值小的值,想到了树状数组。对树从树根进行 d f s dfs dfs,每次 d f s dfs dfs到一个点的时候,先减去当前树状数组中比这个节点值小的个数,最后 d f s dfs dfs完之后再加回去。答案 = - 祖先 + (祖先 + 子节点),所以得出了答案。
#include <bits/stdc++.h>
using namespace std;

#define endl "\n"
#define dbg(x...) do { cout << #x << " -> "; err(x); } while (0)
void err() { cout << endl; }
template <class T, class... Ts>
void err(const T& arg, const Ts&... args) {
    cout << arg << ' '; err(args...);
}

typedef long long ll;
const int maxn = 1e5 + 7;
vector<int> G[maxn];

struct Node{
	int v, id;
}node[maxn];

bool cmp(Node a, Node b) {
	return a.v < b.v;
}
int n, sum[maxn], ans[maxn], id[maxn];
int lowbit(int x) {return x & (-x);}
void add(int i) {
	while (i <= n) {
		sum[i] += 1;
		i += lowbit(i);
	}
}
int query(int i) {
	int res = 0;
	while (i > 0) {
		res += sum[i];
		i -= lowbit(i);
	}
	return res;
}

void dfs(int u) {
	ans[u] -= query(n) - query(id[u]);
	for (auto v : G[u] ) {
		add(id[v]);
		dfs(v);
	}
	ans[u] += query(n) - query(id[u]);
}
void solve() {
	cin >> n;
	for (int i = 1; i <= n; ++i) {
		cin >> node[i].v;
		node[i]. id = i;
	}
	sort(node + 1, node + 1 + n, cmp);
	for (int i = 1; i<= n; ++i) {
		id[node[i].id] = i;
	}
	for (int i = 2, v; i <= n; ++i) {
		cin >> v;
		G[v].push_back(i);
	}
	dfs(1);
	for (int i = 1; i <= n; ++i) {
		cout << ans[i] << endl;
	}
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);cout.tie(nullptr);
	int _T = 1;
	// cin >> _T;
	while (_T--) solve();
	return 0;
}
  • 线段树合并
    对每个子节点建立一棵权值线段树,合并完子树后询问比他大的值的个数。
#include <bits/stdc++.h>
using namespace std;
#define lson t[id].l
#define rson t[id].r

typedef long long ll;

template <class T> inline void read(T &x) {
    int f = 0; x = 0; char ch = getchar();
    for (; !isdigit(ch); ch = getchar()) f |= (ch == '-');
    for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
    if (f) x = -x;
}
const int maxn = 1e5 + 7;

struct Seg{
    int l, r;
    int num;
}t[maxn * 50];
int n, tot, cor[maxn], rt[maxn], a[maxn], c[maxn];
int ans[maxn];
vector<int> G[maxn];

void up(int id) {
    t[id].num = t[lson].num + t[rson].num;
}

void modify(int &id, int l, int r, int p) {
    if(!id) id = ++tot;
    if (l == r) {
        t[id].num++;
        return;
    }
    int mid = l + r >> 1;
    if (p <= mid) modify(lson, l, mid, p);
    else modify(rson, mid + 1, r, p);
    up(id);
}

int merge(int a, int b, int l, int r) {
    if (!a) return b;
    if (!b) return a;
    if (l == r) {
        t[a].num += t[b].num;
        return a;
    }
    int mid = l + r >> 1;
    t[a].l = merge(t[a].l, t[b].l, l, mid);
    t[a].r = merge(t[a].r, t[b].r, mid + 1, r);
    up(a);
    return a;
}

int query(int id, int l, int r, int ql, int qr) {
    int res = 0;
    if (!id || ql > qr) return 0;
    if (ql <= l && qr >= r) return t[id].num;
    int mid = l + r >> 1;
    if (ql <= mid) res += query(lson, l, mid, ql, qr);
    if (qr > mid) res += query(rson, mid + 1, r, ql, qr);
    return res;
}

void dfs(int u) {
    for (auto v : G[u]) {
        dfs(v);
        merge(u, v, 1, n);
    }
    ans[u] = query(u, 1, n, cor[u] + 1, n);
    modify(u, 1, n, cor[u]);
}
void solve() {
    read(n);
    tot = n;
    for (int i = 1; i <= n; ++i) read(a[i]), c[i] = a[i];
    sort(c + 1, c + 1 + n);
    int q = unique(c + 1, c + 1 + n) - c - 1;
    for (int i = 1; i <= n; ++i) {
        cor[i] = lower_bound(c + 1, c + 1 + q, a[i]) - c;
    }
    for (int i = 2, u; i <= n; ++i) {
        read(u);
        G[u].push_back(i);
    }
    dfs(1);
    for (int i = 1; i <= n; ++i) {
        printf("%d\n", ans[i]);
    }
}
int main() {
    int _T = 1;
    while (_T--) solve();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值