题目大意:给定一棵n<=20000的带边权树,求dis(x,y)%3==0的有序点对(x,y)
第一次接触神奇的点分治算法,其主要思想就是对于一棵树,找到他的重心,DP计算所有经过重心点的路径中mod3==0的路径条数,然后将整棵树从重心点处断开,分为若干棵子树,化归解决原问题。其中断树的操作可以通过给边打上bool标记来实现。
/**************************************************************
Problem: 2152
User: RicardoWang
Language: C++
Result: Accepted
Time:332 ms
Memory:3224 kb
****************************************************************/
#include<cstdlib>
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
#define maxn 20005
struct edge
{
int to,d,next; bool ban;
}e[maxn*2];
int n,edge_ct,head[maxn],sz[maxn];
void add(int x,int y,int de)
{
e[++edge_ct]=(edge){y,de,head[x],false}; head[x]=edge_ct;
return ;
}
void _read(int &x)
{
char ch=getchar(); x=0;while(ch<'0'||ch>'9')ch=getchar(); while(ch>='0' && ch<='9'){x=x*10+ch-'0'; ch=getchar();}return ;
}
void Init()
{
_read(n);edge_ct=0;
int x,y,z;
for(int i=1;i<n;i++)
{
_read(x);_read(y); _read(z);
add(x,y,z%3); add(y,x,z%3);
}
return ;
}
void Get_gc(int now,int fa,int size,int &gc)
{
bool flag=1;int j;
sz[now]=1;
for(int id=head[now];id;id=e[id].next)
{
if(e[id].ban || e[id].to==fa)continue;
j=e[id].to;Get_gc(j,now,size,gc);
if(sz[j]>(size>>1))flag=false;
sz[now]+=sz[j];
}
if(size-sz[now]>(size>>1))flag=false;
if(flag)gc=now;
return ;
}
int sum=0;
int cnt[3],_cnt[3];
void DFS(int now,int fa,int dis)
{
_cnt[dis]++; sz[now]=1;
for(int id=head[now];id;id=e[id].next)
{
if(e[id].ban || e[id].to == fa)continue;
DFS(e[id].to,now,(dis+e[id].d)%3);
sz[now]+=sz[e[id].to];
}
return ;
}
void Calc(int now)
{
cnt[0]=1; cnt[1]=0; cnt[2]=0;
for(int id=head[now];id;id=e[id].next)
{
if(e[id].ban)continue;
_cnt[0]=0; _cnt[1]=0; _cnt[2]=0;
DFS(e[id].to,now,e[id].d);
sum+=cnt[0]*_cnt[0]+cnt[1]*_cnt[2]+cnt[2]*_cnt[1];
cnt[0]+=_cnt[0]; cnt[1]+=_cnt[1]; cnt[2]+=_cnt[2];
}
return ;
}
void Tree_Devide(int now,int size)
{
int gc;
Get_gc(now,0,size,gc);
Calc(gc);
for(int id=head[gc];id;id=e[id].next)if(!e[id].ban)
{
e[id].ban=e[id+(id%2==0 ? -1:1)].ban=true;
Tree_Devide(e[id].to,sz[e[id].to]);
}
return ;
}
int gcd(int x,int y)
{
int t;
while(y){t=x%y; x=y; y=t;}
return x;
}
void work()
{
Tree_Devide(1,n);
sum=sum*2+n;
int t=gcd(sum,n*n);
printf("%d/%d\n",sum/t,n*n/t);
return;
}
int main()
{
//freopen("in.txt","r",stdin);
Init();
work();
return 0;
}