无线网络(平衡树启发式合并)

本文详细解析了一种用于计算无线通信网络中城市间通信可能性的算法。在一个由多个城市组成的树状通信网络中,考虑到基站海拔高度的影响,文章提供了一个算法来确定能够进行无线通信的城市对数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

H H H国一共有 n n n座城市,编号为 1 1 1 n n n的正整数。上个月 H H H国已经建成了一个通信网络,将这 n n n座城市通过 n − 1 n-1 n1条通信线路连接起来。为了保证所有城市之间都能互相通信,这 n − 1 n-1 n1跳通信线路组成一个树状的通信网络。显然,对于任意两个城市 x x x y y y,它们之间的同心路径是唯一。
现在,它们希望在此基础上建立一个应急的无线网络。每个城市 i i i都建有一个基站,海拔高度为 h i h_i hi。当两个城市 x x x y y y需要通过无线网络通信时,他们会选择路径上的一个基站 z z z作为中继站(不包括 x x x y y y)。假如三个基站的海拔高度满足 h x + h y ≤ h z h_x+h_y\leq h_z hx+hyhz,那么无线信号就能顺利转发,否则城市 x x x y y y就无法进行无线通信。
现在,已知通行网络的结构以及每个基站的海拔高度,请你求出一共有多少对城市之间能够进行无线通行。

输入格式

输入文件的第一行包含一个整数 n n n,表示城市数量。
接下来的 n n n行,每行包含两个正整数 f i f_i fi h i h_i hi
其中 f i f_i fi 1 1 1 i − 1 i-1 i1之间的正整数(特殊情况 f i = 0 f_i=0 fi=0),表示编号为 i i i的城市与标号为 f i f_i fi的城市之间相连; h i h_i hi表示第 i i i个城市无线基站的海拔。

输出格式

输出文件共一行,包含一个非负整数,表示能够进行通信的城市对数。

样例输入

5 5 5
0 0 0 1 1 1
1 1 1 2 2 2
2 2 2 4 4 4
3 3 3 3 3 3
4 4 4 1 1 1

样例输出

3 3 3

数据范围

数据范围


考试的时候最开始想到的是点分,头脑一热就直接打了“按点分序求满足 h u + h v ≤ h r t h_u+h_v\leq h_{rt} hu+hvhrt的路径数量”,而且还谜一般的过了样例。
然后一看题目,发现事情不对,好像要求的是点对数,显然按照点分序是不对的。
一看时间所剩无几,另外两题还没打过,匆匆改成了“按权值从大到小求满足 h u + h v ≤ h r t h_u+h_v\leq h_{rt} hu+hvhrt的路径数量”。
因为按照点权从大到小枚举,所以路径数量和点对数量是相等的。
时间复杂度 O ( n 2 ∗ log ⁡ n ) O(n^2*\log n) O(n2logn)
竟然还过了 50 % 50\% 50%的数据!!!


考完之后一看题解,嗯,好像只要把枚举的顺序倒过来,再用平衡树维护每个已经遍历过的点构成的连通块(以下简称连通块)内的信息就好了。
先证明一下平衡树合并的时空复杂度:
现在有两棵平衡树 T 1 T_1 T1 T 2 T_2 T2,不妨设 T 1 . s i z e ≤ T 2 . s i z e T_1.size\leq T_2.size T1.sizeT2.size,那么将 T 1 T_1 T1中的每个元素暴力插入到 T 2 T_2 T2中,则新得到的 T 2 ′ . s i z e ≥ 2 ∗ T 1. s i z e T_2^{'}.size\geq2*T1.size T2.size2T1.size
又因为最后的平衡树的大小是 O ( n ) O(n) O(n)的,所以每个元素至多会被插入 O ( n ∗ log ⁡ n ) O(n*\log n) O(nlogn)次。
因为又懒又菜所以写了常数巨大,时间复杂度看脸的 T r e a p Treap Treap
但即使像我这样最最丑 + + +暴力的写法,时间复杂度是 O ( n ∗ log ⁡ 2 n ) O(n*\log^2n) O(nlog2n),空间复杂度是 O ( n ∗ l o g n ) O(n*logn) O(nlogn)的 (据说时空复杂度都可以再少一个 l o g log log,然而本蒟蒻并不会)。
按照点权从小到大遍历,把各棵子树的信息都统计到 s i z e size size最大的那颗平衡树中即可。
还有一个问题,我们遍历当前点 u u u所有未被访问过的相邻节点 v v v时,并不能保证编号为 v v v的平衡树就维护了 v v v所在连通块的信息。
而暴力去找最新的平衡树的编号时间复杂度就不能保证了,所以用并查集维护即可,祖先即为表示当前连通块内值域情况的平衡树编号。
总时间复杂度 O ( n ∗ log ⁡ 2 n ) O(n*\log ^ 2 n) O(nlog2n)(并查集当成常数看了)。


C o d e Code Code

#include <queue>
#include <vector>
#include <cstdio>
#include <algorithm>
#define il inline
#define pb push_back
#define space putchar(' ')
#define enter putchar('\n')
using namespace std;
typedef long long ll;
template <typename T>
void read(T &x) {
	x = 0;
	bool f = 1;
	char ch = getchar();
	while (ch < '0' || ch > '9')
		f &= ch != '-', ch = getchar();
	while (ch >= '0' && ch <= '9')
		x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
	x = f ? x : -x;
}
template <typename T>
void write(T x) {
	if (x < 0)
		x = -x, putchar('-');
	if (x > 9)
		write(x / 10);
	putchar(x % 10 + 48);
}
const int maxn = 3e5 + 5;
const int maxe = 3e5 + 5;
const int maxt = 6e6 + 5;
int ed_cnt;
struct Edge {
	int nxt, to;
} ed[maxe << 1];
int fir[maxn];
int w[maxn];
void clear_edge(int v_cnt) {
	ed_cnt = 0;
	for (int u = 1; u <= v_cnt; ++u)
		fir[u] = 0;
}
il void add_edge(int u, int v) {
	ed[++ed_cnt] = (Edge){ fir[u], v }, fir[u] = ed_cnt;
}
int id[maxn];
il bool cmp(const int &a, const int &b) {
	return w[a] < w[b];
}
int t_cnt;
struct Tree {
	int son[2], val, pri, cnt, siz;
} t[maxt];
class Treap {
private:
	il void update(int p) {
		t[p].siz = t[t[p].son[0]].siz + t[p].cnt + t[t[p].son[1]].siz;
	}
	void rotate(int &p, int d) {
		int k = t[p].son[d];
		t[p].son[d] = t[k].son[d ^ 1];
		t[k].son[d ^ 1] = p;
		update(p), update(p = k);
	}
	il int build(int val) {
		t[++t_cnt] = (Tree){ { 0, 0 }, val, rand(), 1, 1 };
		return t_cnt;
	}
	void _insert(int &p, int val) {
		if (!p) {
			p = build(val);
			return;
		}
		t[p].siz++;
		if (t[p].val == val) {
			t[p].cnt++;
			return;
		}
		int d = t[p].val < val;
		_insert(t[p].son[d], val);
		if (t[p].pri < t[t[p].son[d]].pri)
			rotate(p, d);
	}
	int _lower(int p, int val) {
		if (!p)
			return 0;
		if (t[p].val > val)
			return _lower(t[p].son[0], val);
		if (t[p].val == val)
			return t[t[p].son[0]].siz + t[p].cnt;
		return t[t[p].son[0]].siz + t[p].cnt + _lower(t[p].son[1], val);
	}
public:
	int rt;
	Treap() {
		rt = 0;
	}
	int siz() {
		return t[rt].siz;
	}
	void insert(int val) {
		_insert(rt, val);
	}
	int lower(int val) {
		return _lower(rt, val);
	}
} treap[maxn];
int fa[maxn];
int find(int x) {
	return fa[x] == x ? x : fa[x] = find(fa[x]);
}
bool vis[maxn];
int main() {
	freopen("wireless.in", "r", stdin);
	freopen("wireless.out", "w", stdout);
	int n;
	read(n);
	clear_edge(n);
	for (int u = 1; u <= n; ++u) {
		int v;
		read(v), read(w[u]);
		if (v)
			add_edge(u, v), add_edge(v, u);
	}
	for (int u = 1; u <= n; ++u)
		id[u] = u;
	sort(id + 1, id + n + 1, cmp);
	for (int u = 1; u <= n; ++u)
		fa[u] = u;
	for (int u = 1; u <= n; ++u)
		vis[u] = 0;
	t_cnt = 0;
	ll ans = 0;
	for (int i = 1; i <= n; ++i) {
		int u = id[i];
		vis[u] = 1;
		int mxs = 0, k = 0;
		for (int e = fir[u]; e; e = ed[e].nxt) {
			int v = ed[e].to;
			if (!vis[v])
				continue;
			int x = find(v);
			if (treap[x].siz() > mxs) {
				mxs = treap[x].siz();
				k = v;
			}
		}
		if (k) {
			treap[u].rt = treap[find(k)].rt;
			for (int e = fir[u]; e; e = ed[e].nxt) {
				int v = ed[e].to;
				if(!vis[v] || v == k)
					continue;
				int x = find(v);
				queue<int> q;
				q.push(treap[x].rt);
				vector<int> vec;
				while (!q.empty()) {
					int p = q.front();
					q.pop();
					vec.pb(p);
					if (t[p].son[0])
						q.push(t[p].son[0]);
					if (t[p].son[1])
						q.push(t[p].son[1]);
					ans += treap[u].lower(w[u] - t[p].val) * t[p].cnt;
				}
				for (int i = 0, siz = vec.size(); i < siz; ++i)
					for (int j = 1; j <= t[vec[i]].cnt; ++j)
						treap[u].insert(t[vec[i]].val);
			}
			for (int e = fir[u]; e; e = ed[e].nxt) {
				int v = ed[e].to;
				if (!vis[v])
					continue;
				int x = find(v);
				if (x != u)
					fa[x] = u;
			}
		}
		treap[u].insert(w[u]);
	}
	write(ans), enter;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值