题目大意:给定一棵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; }