思路:基本的点分治思路,num数组记录从u点开始路径长度分别为1或者2或者3的路径长度(取模3意义下),然后做一个简单的容斥就好了。
为了避免计数的麻烦,<u,u>这样的点单独计算,也就是最后的答案加上n就可以了。
bzoj不支持c14,我的代码是在洛谷交的。
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;++i) #define dep(i,b,a) for(int i=b;i>=a;--i) #define clr(a,b) memset(a,b,sizeof(a)) #define pb push_back #define pii pair<int,int > using namespace std; typedef long long ll; ll rd() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const int maxn=20010; const int inf=0x3f3f3f3f; int T,n,m; struct edge{ int to,w; }; vector<edge >ve[maxn]; ll ans=0; int rt,mx,dis[maxn]; ll num[3]; int siz[maxn],size; bool vis[maxn]; int u,v,w; void getrt(int u,int p){ siz[u]=1; int tmp=0; for(auto &st:ve[u]){ int v=st.to; if(vis[v]||v==p)continue; getrt(v,u); siz[u]+=siz[v]; tmp=max(tmp,siz[v]); } tmp=max(size-siz[u],tmp); if(tmp<mx){ mx=tmp; rt=u; } } void getdis(int u,int p){ dis[u]%=3; num[dis[u]]++; for(auto &st:ve[u]){ int v=st.to,w=st.w; if(v==p||vis[v])continue; dis[v]=(dis[u]+w)%3; getdis(v,u); } } ll cal(int u,int val){ dis[u]=val; clr(num,0); getdis(u,0); ll sum=2*num[1]*num[2]+2*(num[0]-1)+(num[0]-1)*(num[0]-1-1); return sum; } void dfs(int u){ vis[u]=1; ans+=cal(u,0); for(auto &st:ve[u]){ int v=st.to,w=st.w; if(vis[v])continue; ans-=cal(v,w); size=siz[v]; mx=inf; getrt(v,0); dfs(rt); } } int main(){ cin>>n; rep(i,1,n-1){ u=rd(),v=rd(),w=rd(); w=w%3; ve[u].pb({v,w}); ve[v].pb({u,w}); } size=n; mx=inf; getrt(1,0); dfs(1); ans+=n; ll an2=1ll*n*n; ll gc=__gcd(an2,ans); printf("%lld/%lld ",ans/gc,an2/gc); }