这是我的点分治第二题,居然自己写1A了!好开心!好开心!好开心!
题目传送门
和上一个题思路一样,每次找重心,统计经过重心的答案,然后再减去子树中的重复的路径,就是答案了。
关于统计,可以dfs子树中所有节点到根的距离,然后输出%3==0的,%3==1的,%3==2的,然后
sum=cnt0∗cnt0+cnt1∗cnt2∗2
,自己想一想,很好懂的。
关于重复路径还是那张图:
不多说了,上代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
int x=0;char ch=' ';int f=1;
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
struct edge{
int to,next,w;
}e[40001];
int n,tot,root,nowsize,mn=0x3f3f3f3f;
int head[20001];
inline void addedge(int x,int y,int l){
e[++tot].to=y;e[tot].next=head[x];e[tot].w=l;head[x]=tot;
}
int dis[20001],vis[20001],size[20001];
void getroot(int x,int fa){
size[x]=1;int num=0;
for(int i=head[x];i;i=e[i].next){
int u=e[i].to;
if(vis[u]||u==fa)continue;
getroot(u,x);
size[x]+=size[u];
num=max(num,size[u]);
}
num=max(num,nowsize-size[x]);
if(num<mn){
mn=num;root=x;
}
}
int q[20001];
int l,r;
void getdis(int x,int fa){
q[++r]=dis[x];
for(int i=head[x];i;i=e[i].next){
int u=e[i].to;
if(vis[u]||u==fa)continue;
dis[u]=dis[x]+e[i].w;
getdis(u,x);
}
}
int calc(int x,int v){
dis[x]=v;r=0;
getdis(x,0);
l=1;
int cnt0=0,cnt1=0,cnt2=0;
for(int i=l;i<=r;i++){
if(q[i]%3==0)cnt0++;
else if(q[i]%3==1)cnt1++;
else cnt2++;
}
int sum=0;
sum+=cnt0*cnt0+cnt1*cnt2*2;
return sum;
}
int ans;
void dfs(int x){
ans+=calc(x,0);
vis[x]=1;
for(int i=head[x];i;i=e[i].next){
int u=e[i].to;
if(vis[u])continue;
ans-=calc(u,e[i].w);
nowsize=size[u];
mn=0x3f3f3f3f;
getroot(u,0);
dfs(root);
}
}
int gcd(int a,int b){
if(b==0)return a;
return gcd(b,a%b);
}
int main(){
n=read();
for(int i=1;i<n;i++){
int x=read(),y=read(),l=read();
addedge(x,y,l);addedge(y,x,l);
}
nowsize=n;
getroot(1,0);
dfs(root);
int down=n*n;
int up=ans;
int t;
while((t=gcd(down,up))!=1){
down/=t;up/=t;
}
printf("%d/%d",up,down);
return 0;
}