bzoj2152 (点分治)
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2152
思路:
要想两点之间距离为3的倍数,那么用t0表示该点距离重心的距离对3取模为0,依此得t1,t2,那么两点之间距离为3的倍数只有三种可能:t1-t2,t2-t1,t0-t0,将所有和重心的具体全部统计好,最后t1*t2*2+t0*t0就是
为3的倍数的点对数量
实现代码:
#include<bits/stdc++.h> using namespace std; #define inf 0x7fffffff const int M = 1e5+10; int siz[M],f[M],head[M],vis[M],ans,cnt,t[M],sum,root,d[M]; struct node{ int to,next,w; }e[M<<1]; void init(){ ans = 0; cnt = 0; memset(vis,0,sizeof(vis)); memset(head,0,sizeof(head)); } void add(int u,int v,int w){ e[++cnt].to = v; e[cnt].w = w; e[cnt].next = head[u]; head[u] = cnt; } void get_root(int u,int fa){ siz[u] = 1; f[u] = 0; for(int i = head[u];i;i = e[i].next){ int v = e[i].to; if(v != fa&&!vis[v]){ get_root(v,u); siz[u] += siz[v]; f[u] = max(f[u],siz[v]); } } f[u] = max(f[u],sum - siz[u]); if(f[u] < f[root]) root = u; return ; } void get_dis(int u,int fa){ t[d[u]]++; for(int i = head[u];i;i = e[i].next){ int v = e[i].to; if(v != fa&& !vis[v]){ d[v] = (d[u] + e[i].w)%3; get_dis(v,u); } } return ; } int cal(int u,int c){ t[0] = t[1] = t[2] = 0; d[u] = c; get_dis(u,0); return t[1]*t[2]*2+t[0]*t[0]; } void solve(int v){ ans += cal(v,0); vis[v] = 1; for(int i = head[v];i;i = e[i].next){ int v = e[i].to; if(!vis[v]){ ans -= cal(v,e[i].w); sum = siz[v]; root = 0; get_root(v,0); solve(root); } } } int main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int m,u,v,w,n; cin>>n; init(); for(int i = 1;i <= n-1;i ++){ cin>>u>>v>>w; w%=3; add(u,v,w); add(v,u,w); } f[0] = inf; sum = n; root = 0; get_root(1,0); solve(root); int x = __gcd(ans,n*n); cout<<ans/x<<"/"<<n*n/x<<endl; return 0; }