这道题的正解——点分治还是挺好想到的,因为题目已经明确给出这道题是以树为基础的。
这题的统计答案与点分治的模板有些不同,不过也还是挺好想到的。
题目要求任意两点的距离是3的倍数的点对数量,我们可以把这个转化为在有根树中,LCA到其中一点的距离为3的倍数余1,到另一节点的距离为3的倍数余2的点对数量加上LCA到两个节点的距离都为3的倍数的点对数量之和。
就此,我们已经解决了这道题,剩下的就是点分治的模板了。
附上AC代码:
#include <cstdio>
#include <cctype>
#include <algorithm>
#define N 20005
using namespace std;
struct side{
int from,to,w,nt;
}s[N<<1];
int n,x,y,w,num,h[N],sum,mx[N],size[N],rt,ans,t[3],d[N];
bool b[N];
inline char nc(){
static char ch[100010],*p1=ch,*p2=ch;
return p1==p2&&(p2=(p1=ch)+fread(ch,1,100010,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &a){
static char c=nc();int f=1;
for (;!isdigit(c);c=nc()) if (c=='-') f=-1;
for (a=0;isdigit(c);a=a*10+c-'0',c=nc());
a*=f;return;
}
inline void add(int x,int y,int w){
s[++num]=(side){x,y,w,h[x]},h[x]=num;
s[++num]=(side){y,x,w,h[y]},h[y]=num;
}
inline void so(int x,int fa){
size[x]=1,mx[x]=0;
for (int i=h[x]; i; i=s[i].nt)
if (!b[s[i].to]&&s[i].to!=fa)
so(s[i].to,x),size[x]+=size[s[i].to],mx[x]=max(mx[x],size[s[i].to]);
mx[x]=max(mx[x],sum-size[x]);
if (mx[x]<mx[rt]) rt=x;
return;
}
inline void pre(int x,int fa){
++t[d[x]];
for (int i=h[x]; i; i=s[i].nt)
if (!b[s[i].to]&&s[i].to!=fa)
d[s[i].to]=(d[x]+s[i].w)%3,pre(s[i].to,x);
return;
}
inline int calc(int x,int v){
t[0]=t[1]=t[2]=0,d[x]=v,pre(x,0);
return t[1]*t[2]*2+t[0]*t[0];
}
inline void work(int x){
ans+=calc(x,0),b[x]=1;
for (int i=h[x]; i; i=s[i].nt)
if (!b[s[i].to]){
ans-=calc(s[i].to,s[i].w),sum=size[s[i].to],rt=0;
so(s[i].to,0),work(rt);
}
return;
}
inline int gcd(int a,int b){
return !b?a:gcd(b,a%b);
}
int main(void){
read(n);
for (int i=1; i<n; ++i) read(x),read(y),read(w),add(x,y,w%3);
sum=mx[0]=n,so(1,0),work(rt);
int t=gcd(ans,n*n);
printf("%d/%d",ans/t,n*n/t);
return 0;
}