点分治
按重心分治,每次暴力统计x下所有子树和的答案,分治进子树的时候减去该子树中的贡献即可
复杂度:nlogn
/**************************************************************
Problem: 2152
User: syh0313
Language: C++
Result: Accepted
Time:556 ms
Memory:3124 kb
****************************************************************/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using
namespace
std;
const
int
maxn=40010;
int
n,st[maxn],to[maxn],nt[maxn],w[maxn],topt;
int
si[maxn],mi,rt,num,nd[5],now,dis[maxn],rem[5];
long
long
a,b;
bool
f[maxn];
inline
void
add(
int
x,
int
y,
int
z)
{to[++topt]=y; nt[topt]=st[x]; st[x]=topt; w[topt]=z;}
void
getrt(
int
x,
int
fa)
{
si[x]=1;
int
p=st[x],ma=0;
while
(p)
{
if
(!f[to[p]] && to[p]!=fa)
{
getrt(to[p],x);
si[x]+=si[to[p]];
if
(si[to[p]]>ma) ma=si[to[p]];
}
p=nt[p];
}
if
(now-si[x]>ma) ma=now-si[x];
if
(ma<mi) {mi=ma; rt=x;}
}
void
getdis(
int
x,
int
fa)
{
nd[dis[x]%3]++;
int
p=st[x];
while
(p)
{
if
(!f[to[p]] && to[p]!=fa) {dis[to[p]]=(dis[x]+w[p])%3; getdis(to[p],x);}
p=nt[p];
}
}
void
solve(
int
x)
{
f[x]=1;
int
p=st[x]; nd[0]=nd[1]=nd[2]=0; nd[0]++;
while
(p)
{
if
(!f[to[p]]) {dis[to[p]]=w[p]; getdis(to[p],x);}
p=nt[p];
}
p=st[x]; a+=1ll*nd[0]*(nd[0]-1)/2; a+=1ll*nd[1]*nd[2];
while
(p)
{
if
(!f[to[p]])
{
dis[to[p]]=w[p]; nd[0]=nd[1]=nd[2]=0; getdis(to[p],x);
a-=1ll*nd[0]*(nd[0]-1)/2; a-=1ll*nd[1]*nd[2];
mi=1e9; now=si[to[p]]; getrt(to[p],x); solve(rt);
}
p=nt[p];
}
}
int
main()
{
scanf
(
"%d"
,&n); b=n*n;
for
(
int
i=1;i<n;i++)
{
int
x,y,z;
scanf
(
"%d%d%d"
,&x,&y,&z);
add(x,y,z%3); add(y,x,z%3);
}
mi=1e9; now=n; getrt(1,0); solve(rt); a*=2ll; a+=n;
printf
(
"%lld/%lld"
,a/__gcd(a,b),b/__gcd(a,b));
return
0;
}