题意:
一棵树上 告诉你边权。
问你有多少点对满足的路上的权值之和 小于等于w, 边数小于等于L。
思路:
显然树上的点分治。
和男人八题一样, 只不过多加了一个边数小于等于L的限制。(一维限制变成二维限制)
做法肯定还是一样。
只不过在求经过重心满足要求点对时有点变化 (其余算法 看上一篇文章)。。
分析一下, 因为边权最大1e9, 边数最大1e5 , 因此, 边数这一个限制,我们可以用数据结构干掉。
因此按照边权w 从小到大排序。
初始状态 把所有的边数 加到 线段树里或者树状数组里, 然后双指针在移动时, 移动一个删一个。
双指针里肯定是边权符合要求的, 要想边数也符合, 直接数据结构查询即可。
注意:
分治的时候就不能在分治一次初始化数据结构一次了。 TLE了两发T_T
分析一下, 初始化数据结构肯定时o(nlogn)的 ,最后双指针肯定移动到一块去, 因此 最后数据结构里最多只有 最后指针那个位置不为空。 log n 删一下即可。
吐槽:
能用树状数组就别用线段树, 慢的两倍多= =!!!!
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 100000 + 10;
int n, l;
long long w;
vector<pair<int,int> >g[maxn];
long long ans;
int vis[maxn];
int siz[maxn];
int mx[maxn];
///=======================
/// 树状数组
int c[maxn];
inline int lowbit(int x){
return x&-x;
}
int SUM(int x){
int ans = 0;
while(x){
ans += c[x];
x -= lowbit(x);
}
return ans;
}
void ADD(int x,int add){
while(x <= l+1){
c[x] += add;
x += lowbit(x);
}
}
///=======================
void getsize(int cur,int fa){
siz[cur] = 1;
mx[cur] = 0;
for (int i = 0; i < g[cur].size(); ++i){
int v = g[cur][i].first;
if (v != fa && !vis[v]){
getsize(v, cur);
siz[cur] += siz[v];
if (siz[v] > mx[cur]){
mx[cur] = siz[v];
}
}
}
}
int root, mi;
void find(int rt, int cur,int fa){ /// 找以rt 为根的树的重心。
mx[cur] = max(mx[cur], siz[rt] - siz[cur]);
if (mx[cur] < mi){
mi = mx[cur];
root = cur;
}
for (int i = 0; i < g[cur].size(); ++i){
int v = g[cur][i].first;
if (v != fa && !vis[v]){
find(rt, v, cur);
}
}
}
struct node{ /// 距离变成了一个二维的偏序关系。
long long d1;
int d2;
node(long long d1 = 0,int d2 = 0):d1(d1), d2(d2){}
};
bool cmp(node a, node b){ /// 按边权排序
return a.d1 < b.d1;
}
node dis[maxn];
int cnt;
void getdis(int cur,long long d1, int d2, int fa){
dis[cnt++] = node(d1, d2);
for (int i = 0; i < g[cur].size(); ++i){
int v = g[cur][i].first;
int w = g[cur][i].second;
if (v != fa && !vis[v]){
getdis(v, d1 + w, d2 + 1, cur);
}
}
}
long long solve(int cur,long long d1,int d2){
long long ret = 0;
cnt = 0;
getdis(cur, d1, d2, 0);
sort(dis, dis + cnt, cmp);
// memset(c,0,sizeof c);
int i = 0, j = cnt - 1;
for (int i = 0; i < cnt; ++i){
ADD(dis[i].d2+1, 1);
}
while(i < j){
ADD(dis[i].d2+1, -1);
while(i < j && dis[i].d1 + dis[j].d1 > w){
ADD(dis[j].d2+1, -1);
--j;
}
if (l - dis[i].d2 >= 0){
int tmp = SUM(l-dis[i].d2+1);
ret += tmp;
}
++i;
}
if (SUM(dis[j].d2 + 1))ADD(dis[j].d2+1,-1); /// *****只需要特判一下 就可以完全清空树状数组,不能memset, 会超时!!!
return ret;
}
const int inf = 0x3f3f3f3f;
void dfs(int cur){
getsize(cur, 0);
mi = inf;
find(cur, cur, 0);
ans += solve(root, 0, 0);
// printf("root = %d\n", root);
vis[root] = 1;
int tmproot = root;
for (int i = 0; i < g[tmproot].size(); ++i){
int v = g[tmproot][i].first;
int w = g[tmproot][i].second;
if (!vis[v]){
ans -= solve(v, w, 1);
dfs(v);
}
}
}
int main(){
scanf("%d %d %lld", &n, &l, &w);
for (int i = 1; i < n; ++i){
int x,y;
scanf("%d %d",&x, &y);
g[x].push_back(make_pair(i+1, y));
g[i+1].push_back(make_pair(x, y));
}
dfs(1);
printf("%lld\n", ans);
return 0;
}