传送门
本蒟蒻AC的第二道点分治,调了
30min
30
m
i
n
发现自己把
gcd
g
c
d
写错了。
这题是一个点分治裸板,记录整颗子树中到根的距离模 3 3 的情况,容斥一下扣去多算的部分就行了。
代码如下:
#include<bits/stdc++.h>
#define N 20005
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+ch-'0',ch=getchar();
return ans;
}
int n,tot,first[N],siz[N],msiz[N],d[N],cnt[3],sum,rt,ans=0;
bool vis[N];
struct Node{int w,v,next;}e[N<<1];
inline void add(int u,int v,int w){e[++tot].v=v,e[tot].w=w,e[tot].next=first[u],first[u]=tot;}
inline void getroot(int p,int fa){
siz[p]=1,msiz[p]=0;
for(int i=first[p];i;i=e[i].next){
int v=e[i].v;
if(v==fa||vis[v])continue;
getroot(v,p),siz[p]+=siz[v];
if(siz[v]>msiz[p])msiz[p]=siz[v];
}
msiz[p]=max(msiz[p],sum-siz[p]);
if(msiz[p]<msiz[rt])rt=p;
}
inline void getdis(int p,int fa){
++cnt[d[p]];
for(int i=first[p];i;i=e[i].next){
int v=e[i].v;
if(v==fa||vis[v])continue;
d[v]=(d[p]+e[i].w)%3,getdis(v,p);
}
}
inline int calc(int p,int v){
cnt[0]=cnt[1]=cnt[2]=0,d[p]=v,getdis(p,0);
return cnt[0]*cnt[0]+cnt[1]*cnt[2]*2;
}
inline void solve(int p){
ans+=calc(p,0);
vis[p]=true;
for(int i=first[p];i;i=e[i].next){
int v=e[i].v;
if(vis[v])continue;
ans-=calc(v,e[i].w);
rt=0,sum=siz[v];
getroot(v,0);
solve(rt);
}
}
inline int gcd(int a,int b){while(b){int t=a;a=b,b=t%a;}return a;}
int main(){
memset(vis,false,sizeof(vis));
n=read();
for(int i=1;i<n;++i){
int u=read(),v=read(),w=read()%3;
add(u,v,w),add(v,u,w);
}
rt=0,msiz[0]=sum=n,getroot(1,0),solve(rt);
int g=gcd(ans,n*n);
printf("%d/%d",ans/g,n*n/g);
return 0;
}