题目描述
HHH国一共有nnn座城市,编号为111到nnn的正整数。上个月HHH国已经建成了一个通信网络,将这nnn座城市通过n−1n-1n−1条通信线路连接起来。为了保证所有城市之间都能互相通信,这n−1n-1n−1跳通信线路组成一个树状的通信网络。显然,对于任意两个城市xxx和yyy,它们之间的同心路径是唯一。
现在,它们希望在此基础上建立一个应急的无线网络。每个城市iii都建有一个基站,海拔高度为hih_ihi。当两个城市xxx和yyy需要通过无线网络通信时,他们会选择路径上的一个基站zzz作为中继站(不包括xxx和yyy)。假如三个基站的海拔高度满足hx+hy≤hzh_x+h_y\leq h_zhx+hy≤hz,那么无线信号就能顺利转发,否则城市xxx和yyy就无法进行无线通信。
现在,已知通行网络的结构以及每个基站的海拔高度,请你求出一共有多少对城市之间能够进行无线通行。
输入格式
输入文件的第一行包含一个整数nnn,表示城市数量。
接下来的nnn行,每行包含两个正整数fif_ifi和hih_ihi。
其中fif_ifi为111到i−1i-1i−1之间的正整数(特殊情况fi=0f_i=0fi=0),表示编号为iii的城市与标号为fif_ifi的城市之间相连;hih_ihi表示第iii个城市无线基站的海拔。
输出格式
输出文件共一行,包含一个非负整数,表示能够进行通信的城市对数。
样例输入
555
000 111
111 222
222 444
333 333
444 111
样例输出
333
数据范围
考试的时候最开始想到的是点分,头脑一热就直接打了“按点分序求满足hu+hv≤hrth_u+h_v\leq h_{rt}hu+hv≤hrt的路径数量”,而且还谜一般的过了样例。
然后一看题目,发现事情不对,好像要求的是点对数,显然按照点分序是不对的。
一看时间所剩无几,另外两题还没打过,匆匆改成了“按权值从大到小求满足hu+hv≤hrth_u+h_v\leq h_{rt}hu+hv≤hrt的路径数量”。
因为按照点权从大到小枚举,所以路径数量和点对数量是相等的。
时间复杂度O(n2∗logn)O(n^2*\log n)O(n2∗logn)
竟然还过了50%50\%50%的数据!!!
考完之后一看题解,嗯,好像只要把枚举的顺序倒过来,再用平衡树维护每个已经遍历过的点构成的连通块(以下简称连通块)内的信息就好了。
先证明一下平衡树合并的时空复杂度:
现在有两棵平衡树T1T_1T1和T2T_2T2,不妨设T1.size≤T2.sizeT_1.size\leq T_2.sizeT1.size≤T2.size,那么将T1T_1T1中的每个元素暴力插入到T2T_2T2中,则新得到的T2′.size≥2∗T1.sizeT_2^{'}.size\geq2*T1.sizeT2′.size≥2∗T1.size。
又因为最后的平衡树的大小是O(n)O(n)O(n)的,所以每个元素至多会被插入O(n∗logn)O(n*\log n)O(n∗logn)次。
因为又懒又菜所以写了常数巨大,时间复杂度看脸的TreapTreapTreap。
但即使像我这样最最丑+++暴力的写法,时间复杂度是O(n∗log2n)O(n*\log^2n)O(n∗log2n),空间复杂度是O(n∗logn)O(n*logn)O(n∗logn)的 (据说时空复杂度都可以再少一个logloglog,然而本蒟蒻并不会)。
按照点权从小到大遍历,把各棵子树的信息都统计到sizesizesize最大的那颗平衡树中即可。
还有一个问题,我们遍历当前点uuu所有未被访问过的相邻节点vvv时,并不能保证编号为vvv的平衡树就维护了vvv所在连通块的信息。
而暴力去找最新的平衡树的编号时间复杂度就不能保证了,所以用并查集维护即可,祖先即为表示当前连通块内值域情况的平衡树编号。
总时间复杂度O(n∗log2n)O(n*\log ^ 2 n)O(n∗log2n)(并查集当成常数看了)。
CodeCodeCode
#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;
}