题目描述
题解
树上的路径问题可以通过点分治来解决。但是关键问题是“3的倍数”如何满足?
假设两点的距离记为两点到根的深度之和,那么一棵树内的合法点对可以表示为:(所有深度%3=0的点)^2+所有深度%3=1的点*所有深度%3=2的点*2。正确性显然。
那么每一次遍历只需要统计每个点深度%3的值就可以了,每一次的结果都可以
O(1)
计算。
总时间复杂度为
O(nlogn)
。
代码
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define N 20005
#define INF 2000000000
int n,x,y,z,sum,root,ans;
int tot,point[N],nxt[N*2],v[N*2],c[N*2];
int size[N],big[N],d[N],t[3];
bool vis[N];
void addedge(int x,int y,int z)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=z;
}
void getroot(int x,int fa)
{
size[x]=1; big[x]=0;
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa&&!vis[v[i]])
{
getroot(v[i],x);
size[x]+=size[v[i]];
big[x]=max(big[x],size[v[i]]);
}
big[x]=max(big[x],sum-size[x]);
if (big[x]<big[root]) root=x;
}
void getdeep(int x,int fa)
{
t[d[x]]++;
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa&&!vis[v[i]])
{
d[v[i]]=(d[x]+c[i])%3;
getdeep(v[i],x);
}
}
int calc(int x,int now)
{
d[x]=now%3;
t[0]=t[1]=t[2]=0;
getdeep(x,0);
return t[1]*t[2]*2+t[0]*t[0];
}
void dfs(int x)
{
ans+=calc(x,0);
vis[x]=true;
for (int i=point[x];i;i=nxt[i])
if (!vis[v[i]])
{
ans-=calc(v[i],c[i]);
sum=size[v[i]]; root=0;
getroot(v[i],0);
dfs(root);
}
}
int gcd(int a,int b)
{
if (!b) return a;
else return gcd(b,a%b);
}
int main()
{
scanf("%d",&n);
for (int i=1;i<n;++i)
{
scanf("%d%d%d",&x,&y,&z);
z%=3;
addedge(x,y,z);
}
sum=n; root=0; big[0]=INF;
getroot(1,0);
dfs(root);
if (!ans)
{
puts("0/0");
return 0;
}
int t=gcd(ans,n*n);
printf("%d/%d",ans/t,n*n/t);
}