[国家集训队] 聪聪可可
算是点分治的模板吧,把每条边都模3归类就可以了。
模板我是学yyb dalao的。
计算calc函数的时候有一点改进就是不需要把所有的节点的枚举一遍了,可以直接通过相互关系计算得出qwq。
至于为什么前面不同的要乘以二呢?大家可以思考一下,原先我们要枚举每个点然后是一个平方的算法,然后自然是值不同的话会被计算两次,值相同的只有一次啦qwq(不理解的话可以模拟一下过程??)
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 100010
using namespace std;
inline int read()
{
int f=1,x=0; char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
int n,m,t,tot,root,minr,size;
int done[MAXN],head[MAXN],s[4],siz[MAXN],num[4];
struct Edge{int nxt,to,dis;}edge[MAXN<<1];
inline void add(int from,int to,int dis){edge[++t].nxt=head[from],edge[t].to=to,edge[t].dis=dis,head[from]=t;}
inline void get_rt(int x,int fa)
{
siz[x]=1;
int res=0;
for(int i=head[x];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(v==fa||done[v]) continue;
get_rt(v,x);
res=max(res,siz[v]);
siz[x]+=siz[v];
}
res=max(res,size-siz[x]);
if(res<minr) minr=res,root=x;
}
inline void get_dep(int x,int fa,int dep)
{
s[dep%3]++;
for(int i=head[x];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(v==fa||done[v]) continue;
get_dep(v,x,dep+edge[i].dis);
}
}
inline void calc(int x,int op,int sum)
{
memset(s,0,sizeof(s));
get_dep(x,x,0);
if(op)
{
num[0]+=2*s[1]*s[2]+s[0]*s[0];
num[1]+=2*s[0]*s[1]+s[2]*s[2];
num[2]+=2*s[0]*s[2]+s[1]*s[1];
}
else
{
sum%=3;
num[(0+sum)%3]-=2*s[1]*s[2]+s[0]*s[0];
num[(1+sum)%3]-=2*s[0]*s[1]+s[2]*s[2];
num[(2+sum)%3]-=2*s[0]*s[2]+s[1]*s[1];
}
}
inline void dfs(int x)
{
calc(x,1,0);
done[x]=1;
for(int i=head[x];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(done[v]) continue;
calc(v,0,edge[i].dis*2);
minr=n,size=siz[v];
get_rt(v,x);
dfs(root);
}
}
inline int gcd(int x,int y)
{
return y?gcd(y,x%y):x;
}
int main()
{
n=read();
int u,v,w;
for(int i=1;i<n;i++)
{
u=read(),v=read(),w=read();
add(u,v,w),add(v,u,w);
}
minr=size=n;
get_rt(1,1);
dfs(root);
int x=num[0];
int y=num[0]+num[1]+num[2];
int cur=gcd(x,y);
printf("%d/%d\n",x/cur,y/cur);
return 0;
}