题目大意:
给你一棵有
个点的树,每条边有边权
,定义两个点
的距离为简单路径上的边数,权重为简单路径上的权值和。求有多少对
,其中
,距离小于等于
,权重小于等于
。
分析:
两点之间的距离和权重显然可以想到点分治了。考虑一棵子树对于前面的子树的贡献。先只考虑一维距离,我们可以把当前子树节点的到当前根的距离从大到小排序,把前面的子树的节点从小到大排序,这时候只需要分别使用一个指针就可以了。当一个指针右移使距离变小时,另一个指针右移增大,然后统计答案即可。复杂度是与节点数线性相关的。
对于两维的话,第一维还是按照上述方法,而第二个指针右移时,把这些节点的第二维丢到一个树状数组里,然后就可以统计答案了。由于距离最大只有 ,而权值能达到 ,所以第一维应该设为权值,第二维应该设为距离。
代码:
#include <iostream>
#include <cmath>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
#define LL long long
const int maxn=1e5+7;
using namespace std;
int n,l,d,x,w,cnt,root,sum,head,tail;
int t[maxn];
LL ans;
int size[maxn],vis[maxn],dis[maxn],len[maxn],f[maxn];
int ls[maxn];
struct edge{
int y,w,next;
}g[maxn*2];
struct rec{
int l,d;
}h[maxn];
queue <int> q;
void add(int x,int y,int w)
{
g[++cnt]=(edge){y,w,ls[x]};
ls[x]=cnt;
}
bool cmp1(rec x,rec y)
{
return x.d<y.d;
}
bool cmp2(rec x,rec y)
{
return x.d>y.d;
}
void findroot(int x,int fa)
{
size[x]=1;
f[x]=0;
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
if ((y==fa) || (vis[y])) continue;
findroot(y,x);
size[x]+=size[y];
f[x]=max(f[x],size[y]);
}
f[x]=max(f[x],sum-size[x]);
if ((f[x]<f[root]) || (!root)) root=x;
}
int lowbit(int x)
{
return x&(-x);
}
void updata(int x,int k)
{
while (x<=n)
{
t[x]+=k;
x+=lowbit(x);
}
}
int getsum(int x)
{
int sum=0;
while (x>0)
{
sum+=t[x];
x-=lowbit(x);
}
return sum;
}
void dfs(int x,int fa)
{
size[x]=1;
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
if ((y==fa) || (vis[y])) continue;
len[y]=len[x]+1;
dis[y]=dis[x]+g[i].w;
h[++tail]=(rec){len[y],dis[y]};
dfs(y,x);
size[x]+=size[y];
if (x==root)
{
sort(h+1,h+head+1,cmp1);
sort(h+head+1,h+tail+1,cmp2);
int k=1;
for (int j=head+1;j<=tail;j++)
{
while ((h[k].d+h[j].d<=d) && (k<=head))
{
updata(h[k].l+1,1);
k++;
}
ans+=(LL)getsum(l-h[j].l+1);
}
for (int g=1;g<k;g++) updata(h[g].l+1,-1);
head=tail;
q.push(y);
}
}
}
int main()
{
scanf("%d%d%d",&n,&l,&d);
for (int i=2;i<=n;i++)
{
scanf("%d%d",&x,&w);
add(i,x,w);
add(x,i,w);
}
q.push(1);
size[1]=n;
while (!q.empty())
{
int x=q.front();
q.pop();
sum=size[x];
root=0;
findroot(x,0);
len[root]=dis[root]=0;
head=1; tail=1;
h[1]=(rec){0,0};
dfs(root,0);
vis[root]=1;
}
printf("%lld",ans);
}