[BZOJ 2152] 聪聪可可
Link:
Solution:
直接上点分治就行了(其实也可以树形$dp$)
其中对于穿过重心的边的统计类似于树形$dp$的计算方式:
将当前子树中模3分别为0/1/2的边与之前所有子树产生的边进行组合
Code:
//by NewErA #include <bits/stdc++.h> using namespace std; typedef pair<int,int> P; const int INF=1<<27; const int MAXN=20005; vector<P> G[MAXN]; int x,y,z,n,sz[MAXN],vis[MAXN],mx_sub[MAXN],v_sum=0,res=0,root; int dist[MAXN],f[MAXN][5]; void getroot(int x,int anc) { sz[x]=1;mx_sub[x]=0; //mx_sub要重新初始化 for(int i=0;i<G[x].size();i++) { int v=G[x][i].first; if(v==anc || vis[v]) continue; getroot(v,x);sz[x]+=sz[v]; mx_sub[x]=max(mx_sub[x],sz[v]); } mx_sub[x]=max(mx_sub[x],v_sum-sz[x]); if(mx_sub[x]<mx_sub[root]) root=x; } void cal(int x,int anc,int val,int pos) { f[pos][val%3]++; for(int i=0;i<G[x].size();i++) { int v=G[x][i].first; if(v==anc || vis[v]) continue; cal(v,x,val+G[x][i].second,pos); } } void solve(int x) { vis[x]=true; for(int i=0;i<G[x].size();i++) { int v=G[x][i].first; if(vis[v]) continue; int t0=f[x][0],t1=f[x][1],t2=f[x][2]; cal(v,x,G[x][i].second,x); res+=(t0+1)*(f[x][0]-t0)+t2*(f[x][1]-t1)+t1*(f[x][2]-t2); v_sum=sz[v];getroot(v,root=0); solve(root);//注意这里的变量名 } } int GCD(int a,int b){return (a%b)?GCD(b,a%b):b;} int main() { scanf("%d",&n); for(int i=1;i<n;i++) { scanf("%d%d%d",&x,&y,&z); G[x].push_back(P(y,z%3));G[y].push_back(P(x,z%3)); } mx_sub[0]=v_sum=n,getroot(1,root=0); solve(root);res=res*2+n; int gcd=GCD(res,n*n); cout << res/gcd << '/' << n*n/gcd; return 0; }