题目:BZOJ2152
解析:
点分治/树形DP。
很明显的点分治,主要说下统计答案的问题。
假设两点的距离记为两点到当前重心的深度之和,那么对于经过重心的合法点对可以表示为:
s
u
m
[
0
]
∗
(
s
u
m
[
0
]
−
1
)
+
s
u
m
[
0
]
+
s
u
m
[
1
]
∗
s
u
m
[
2
]
∗
2
sum[0]*(sum[0]-1)+sum[0]+sum[1]*sum[2]*2
sum[0]∗(sum[0]−1)+sum[0]+sum[1]∗sum[2]∗2
s
u
m
[
0
]
,
s
u
m
[
1
]
,
s
u
m
[
2
]
sum[0],sum[1],sum[2]
sum[0],sum[1],sum[2]表示模
3
3
3为
0
,
1
,
2
0,1,2
0,1,2的点数量。因为
(
u
,
v
)
,
(
v
,
u
)
(u,v),(v,u)
(u,v),(v,u)是算两次的,
(
u
,
u
)
(u,u)
(u,u)是算一次的。
代码:
#include <bits/stdc++.h>
using namespace std;
const int Max=20005;
int n,m,s,rt,ans,sum,tot;
int first[Max],size[Max],num[Max],vis[Max],d[Max],ss[5];
struct shu{int to,next,len;}e[Max<<1];
inline int get_int()
{
int x=0,f=1;char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='-') f=-1,c=getchar();
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
inline void build(int x,int y,int z)
{
e[++s].next=first[x],first[x]=s,e[s].to=y,e[s].len=z;
e[++s].next=first[y],first[y]=s,e[s].to=x,e[s].len=z;
}
inline void getp(int p,int fa)
{
size[p]=1;int maxx=0;
for(int u=first[p];u;u=e[u].next)
{
int to=e[u].to;
if(vis[to]||to==fa) continue;
getp(to,p),size[p]+=size[to],maxx=max(maxx,size[to]);
}
maxx=max(maxx,sum-size[p]);
if(maxx<m) m=maxx,rt=p;
}
inline void getd(int p,int fa)
{
ss[d[p]]++;
for(int u=first[p];u;u=e[u].next)
{
int to=e[u].to;
if(to==fa||vis[to]) continue;
d[to]=(d[p]+e[u].len)%3,getd(to,p);
}
}
inline int calc(int p,int w)
{
d[p]=w,ss[2]=ss[1]=ss[0]=0,getd(p,0);
return ss[1]*ss[2]*2+ss[0]*ss[0];
}
inline void dfs(int p)
{
ans+=calc(p,0),vis[p]=1;
for(int u=first[p];u;u=e[u].next)
{
int to=e[u].to;
if(vis[to]) continue;
ans-=calc(to,e[u].len),m=1e9,sum=size[to],getp(to,rt),dfs(rt);
}
}
inline void init()
{
n=get_int();
for(int i=1;i<n;i++)
{
int x=get_int(),y=get_int(),z=get_int()%3;
build(x,y,z);
}
}
inline void solve()
{
m=1e9,sum=n,getp(1,0),dfs(rt);
cout<<(ans/__gcd(ans,n*n))<<"/"<<(n*n/__gcd(ans,n*n));
}
int main()
{
init();
solve();
return 0;
}