传送门biu~
树的点分治。把0边权改为-1,记录出现过休息点的路径数目和没出现过休息点的路径数目。答案即为两条路径都有休息点的方案数加上只有一个休息点的方案数。
#include<bits/stdc++.h>
#define max(x,y) (x>y?x:y)
#define min(x,y) (x<y?x:y)
#define N 100005
using namespace std;
int n,center,Max,Min;
int head[N],nex[2*N],to[2*N],v[2*N],tp;
int siz[N],num[4*N],fa[N];
int g[4*N][2],f[4*N][2];
long long ans;
bool b[N];
inline void add(int x,int y,int z){
nex[++tp]=head[x];
head[x]=tp;
to[tp]=y;
v[tp]=z;
}
void get_tree_center(int x,int father,int tot){
fa[x]=father;
siz[x]=1;
bool flag=true;
for(int i=head[x];i;i=nex[i]){
if(to[i]==father || b[to[i]]) continue;
get_tree_center(to[i],x,tot);
if(siz[to[i]]>tot/2) flag=false;
siz[x]+=siz[to[i]];
}
if(tot-siz[x]>tot/2) flag=false;
if(flag) center=x;
}
void dfs(int x,int father,int val){
Max=max(Max,val);
Min=min(Min,val);
if(num[val]) ++f[val][1];
else ++f[val][0];
++num[val];
for(int i=head[x];i;i=nex[i]){
if(to[i]==father || b[to[i]]) continue;
dfs(to[i],x,val+v[i]);
}
--num[val];
}
void tree_divide(int x){
int tot=siz[x];
get_tree_center(x,0,siz[x]);
x=center;
if(fa[x]) siz[fa[x]]=tot-siz[x];
g[200000][0]=1;
int maxx=0,minn=400000;
for(int i=head[x];i;i=nex[i]){
if(b[to[i]]) continue;
Max=0;Min=400000;
dfs(to[i],x,200000+v[i]);
maxx=max(maxx,Max);
minn=min(minn,Min);
ans+=1ll*(g[200000][0]-1)*f[200000][0];
for(int j=Min;j<=Max;++j)
ans+=1ll*f[j][0]*g[400000-j][1]+1ll*f[j][1]*g[400000-j][0]+1ll*f[j][1]*g[400000-j][1];
for(int j=Min;j<=Max;++j) g[j][0]+=f[j][0],g[j][1]+=f[j][1],f[j][0]=f[j][1]=0;
}
for(int i=minn;i<=maxx;++i) g[i][0]=g[i][1]=0;
g[200000][0]=0;
b[x]=1;
for(int i=head[x];i;i=nex[i]){
if(b[to[i]]) continue;
tree_divide(to[i]);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<n;++i){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
if(!z) z=-1;
add(x,y,z);
add(y,x,z);
}
siz[1]=n;
tree_divide(1);
printf("%lld",ans);
return 0;
}