http://codeforces.com/problemset/problem/1213/G
题目大意:给一棵树,对于每个询问
q
i
q_{i}
qi,输出满足从
u
u
u到
v
v
v的路径上的最大边权
<
=
q
i
<=q_{i}
<=qi的点对
(
u
,
v
)
(u,v)
(u,v)的数量(且
u
<
v
u<v
u<v)。
思路:设一堆点的个数为
n
u
m
num
num,若从该堆点中任选两个点的路径上的最大边权均
<
=
q
i
<=q_{i}
<=qi,那么这堆点对答案
a
n
s
i
ans_{i}
ansi的贡献就是
n
u
m
∗
(
n
u
m
−
1
)
∗
/
2
num*(num-1)*/2
num∗(num−1)∗/2。可以用并查集维护这些点,对于连接
u
u
u和
v
v
v的一条边,若其边权
<
=
q
i
<=q_{i}
<=qi,那么我们就把它们合并到同一集合中,然后计算贡献(此处要消去合并前的贡献 具体见代码)。如果询问
q
i
−
1
<
=
q
i
q_{i-1}<=q_{i}
qi−1<=qi,那么
a
n
s
i
−
1
ans_{i-1}
ansi−1的贡献一定可以累加到
a
n
s
i
ans_{i}
ansi中,因此把询问离线从小到大排序,就可以递推得到结果。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 0x3f3f3f3f
#define pr pair<int,int>
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
int f[maxn];
ll num[maxn];
ll ans[maxn];
int n,m;
struct Edge
{
int from,to,dis;
bool operator <(const Edge &a)const
{
return dis<a.dis;
}
}edge[maxn];
struct query
{
int id,v;
bool operator <(const query &a)const
{
return v<a.v;
}
}q[maxn];
int father(int x)
{
return f[x]==x?x:f[x]=father(f[x]);
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=0;i<n-1;i++)
scanf("%d %d %d",&edge[i].from,&edge[i].to,&edge[i].dis);
sort(edge,edge+n-1);
for(int i=0;i<m;i++)
q[i].id=i,scanf("%d",&q[i].v);
sort(q,q+m);
for(int i=1;i<=n;i++)
f[i]=i,num[i]=1;
int idx=0;
int u,v,fu,fv;
ll sum=0;
for(int i=0;i<m;i++)
{
while(q[i].v>=edge[idx].dis&&idx<n-1)
{
u=edge[idx].from,v=edge[idx].to;
fu=father(u),fv=father(v);
if(fu!=fv)
{
sum-=num[fu]*(num[fu]-1)/2;
sum-=num[fv]*(num[fv]-1)/2;
num[fu]+=num[fv],f[fv]=fu;
sum+=num[fu]*(num[fu]-1)/2;
}
++idx;
}
ans[q[i].id]=sum;
}
for(int i=0;i<m;i++)
printf("%lld ",ans[i]);
return 0;
}