1.Spaly的启发式合并
2.点分治算法
3.线段树合并
4.树链剖分
/*
一开始每一个节点都是一棵Splay。
对于一个节点u,我们将子节点v都启发式合并到节点u上,
并将v这棵Splay权值整体加上w,然后统计两棵Spaly的节点所形成的答案,
即对于v中的每一个节点,查询 pre(K - tr[x].v) 的个数
*/
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 2e5 + 10, INF = 0x3f3f3f;
struct Tree {
int p, v, s[2], siz;
int tag;
void init(int _v) {
siz = 1;
v = _v;
}
} tr[N];
struct Edge {
int v, w, nxt;
} e[N << 1];
int fa[N], n, m, head[N], cnt, idx, root[N], siz[N], num[N];
void pushup(int u)
{
tr[u].siz = tr[tr[u].s[0]].siz + tr[tr[u].s[1]].siz + 1;
}
void pushdown(int u)
{
if( tr[u].tag) {
Tree & l = tr[tr[u].s[0]], & r = tr[tr[u].s[1]];
l.tag += tr[u].tag, l.v += tr[u].tag;
r.tag += tr[u].tag, r.v += tr[u].tag;
tr[u].tag = 0;
}
}
void AddEdge(int u, int v, int w)
{
e[ ++cnt].v = v;
e[cnt].w = w;
e[cnt].nxt = head[u];
head[u] = cnt;
}
bool dir(int x) {
return tr[tr[x].p].s[1] == x;
}
void con(int x, int y, int son)
{
tr[y].s[son] = x, tr[x].p = y;
}
void rotate(int x)
{
int y = tr[x].p, ys = dir(x);
int z = tr[y].p, zs = dir(y);
int B = tr[x].s[ys ^ 1];
con(B, y, ys), con(y, x, ys ^ 1), con(x, z, zs);
}
void splay(int i, int x, int k)
{
while(tr[x].p != k) {
int y = tr[x].p;
if( tr[y].p != k)
rotate(dir(x) == dir(y) ? y : x);
rotate(x);
}
if( !k) root[i] = x;
}
int get_root(int x)
{
return fa[x] == x ? x : get_root(fa[x]);
}
void insert(int i, int v)
{
int u = root[i], p = 0;
while(u) p = u, u = tr[u].s[v > tr[u].v];
u = ++ idx;
tr[u].init(v);
con(u, p, v > tr[p].v);
splay(i, u, 0);
}
int get_pre(int i, int v) {
int u = root[i], p = 0;
while(u) {
pushdown(u);
if( v >= tr[u].v)
p = u, u = tr[u].s[1];
else u = tr[u].s[0];
}
if(!p) return INF;
splay(p, 0, i);
return tr[tr[p].s[0]].siz + 1;
}
void dfs2(int rt, int u)//将u合并到rt上
{
pushdown(u);
if( tr[u].s[0]) dfs2(rt, tr[u].s[0]);
if( tr[u].s[1]) dfs2(rt, tr[u].s[1]);
insert(root[rt], tr[u].v);
num[rt] += get_pre(rt, m - tr[u].v);
}
int dfs1(int u, int p)
{
int rt = u;
for(int i = head[u]; i; i = e[i].nxt) {
int v = e[i].v;
if( v == p) continue;
int t = dfs1(v, u);//返回合并后并查集的树根
tr[root[t]].tag += e[i].w;
tr[root[t]].v += e[i].w;
rt = get_root(u);
if( siz[rt] < siz[t]) swap(rt, t);
fa[t] = rt;
siz[rt] += siz[t];
num[rt] += num[t];
dfs2(rt, root[t]);
}
return rt;
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i ++) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
AddEdge(u, v, w), AddEdge(v, u, w);
fa[i] = i, siz[i] = 1;
}
for(int i = 1; i <= n; i ++) {
root[i] = i;
tr[i].init(v);
}
scanf("%d", &m);
idx = n;
dfs1(1, 0);
printf("%d
", num[get_root(1)]);
return 0;
}