【題解】聰聰可可
阿新 • • 發佈:2020-08-21
\(\text{Solution:}\)
顯然題目所求和“大規模處理樹上路徑問題”這一特點相符。考慮點分治。
由於題目只要求對於\(3\)的倍數,所以我們可以分別記錄\(tmp[i]\)表示到當前點路徑長度為\(i\)的路徑數目。\(i\in \text{{0,1,2}}\)
若我們知道了這三個量,則此處的答案就是\(tmp[0]^2+2*tmp[1]*tmp[2].\)
\(tmp[1]*tmp[2]\)之所以要乘以\(2\)是因為對於點對\((u,v),(v,u)\)它們算作兩種。
\(tmp[0]^2\)之所以不需要乘以\(2,\)是因為它本身就是一個集合的自我組合。也就是說在平方的過程中,我們已經把上述情況考慮過了。
\(tmp[0]^2\)的組合意義就是從\(tmp[0]\)中任選兩個點(注意選擇點可以重合)相組合。而\(tmp[1]*tmp[2]\)雖然也是這個意思,但由於它們分別屬於兩個不同集合,所以我們最後計數需要把它們乘以\(2.\)
剩下的就是點分治模板了。找重心,計數的時候容斥一下,最後寫個\(gcd\)就過了。
#include<bits/stdc++.h> using namespace std; const int MAXN=100010; inline int gcd(int x,int y){return !y?x:gcd(y,x%y);} int tot,head[MAXN],siz[MAXN],Siz,ans,n; struct edge{int nxt,to,dis;}e[MAXN]; inline void link(int x,int y,int w){e[++tot].to=y;e[tot].nxt=head[x];e[tot].dis=w;head[x]=tot;} inline int add(int x,int y){return ((x+y)%3);}; int mson[MAXN],ms,rt,dis[MAXN]; const int inf=(1<<30); bitset<MAXN>vis; void Gr(int x,int fa){ siz[x]=1;mson[x]=0; for(int i=head[x];i;i=e[i].nxt){ int j=e[i].to; if(vis[j]||j==fa)continue; Gr(j,x);siz[x]+=siz[j]; if(siz[j]>mson[x])mson[x]=siz[j]; } if(Siz-siz[x]>mson[x])mson[x]=Siz-siz[x]; if(ms>mson[x])ms=mson[x],rt=x; } int t,tmp[4]; void Getdis(int x,int fa,int d){ dis[++t]=(d%3);tmp[dis[t]]++; for(int i=head[x];i;i=e[i].nxt){ int j=e[i].to; if(j==fa||vis[j])continue; Getdis(j,x,add(d,e[i].dis)); } } int solve(int x,int d){ t=0;tmp[1]=tmp[0]=tmp[2]=0; Getdis(x,0,d);return (tmp[0]*tmp[0]+tmp[1]*tmp[2]*2); } void work(int x,int s){ vis[x]=1;ans+=solve(x,0); for(int i=head[x];i;i=e[i].nxt){ int j=e[i].to; if(vis[j])continue; ans-=solve(j,e[i].dis); ms=inf;rt=0;Siz=siz[j]<siz[x]?siz[x]:(s-siz[x]); Gr(j,0);work(rt,Siz); } } int main(){ scanf("%d",&n); for(int i=1;i<n;++i){ int x,y,z; scanf("%d%d%d",&x,&y,&z); link(x,y,z);link(y,x,z); } rt=0;ms=inf;Siz=n;Gr(1,0);work(rt,n); int g=gcd(n*n,ans); printf("%d",ans/g);putchar('/'); printf("%d\n",n*n/g); return 0; }