LGP2634 [國家集訓隊]聰聰可可
阿新 • • 發佈:2021-06-16
LGP2634 [國家集訓隊]聰聰可可
思路
1.點分治,每次歸併的時候,兩點的距離和對\(3\)取餘,用桶來記錄出現\(0、1、2\)的次數,對於路徑中兩個點在同一顆子樹中的路徑,方案數為\(mp[1]*mp[2]+(mp[0]*(mp[0]-1) )/2\),最後所有子樹合併時,容斥一下每棵子樹的貢獻。最後由於題目求的時排列而非組合,且包括自身與自身組成的排列,所以最後\(ans=ans*2+n\)。
2.分子求出來後,分母顯然為\(n*n\)。
Code:
#include<bits/stdc++.h> using namespace std; int n,m,k; const int N=2e4+10; struct node{ int to; int next; int dis; }edge[N<<1]; int head[N],num_edge,u,v,_d,base; bool st[N]; void edge_add(int from,int to,int dis){ edge[++num_edge].next=head[from]; edge[num_edge].to=to; edge[num_edge].dis=dis; head[from]=num_edge; } int get_size(int u,int fa){ if(st[u])return 0; int res=1; for(int i=head[u];i;i=edge[i].next){ int to=edge[i].to; if(to==fa)continue; res+=get_size(to,u); } return res; } int get_wc(int u,int fa,int tot,int &wc){ if(st[u])return 0; int res=1; int mx=0; for(int i=head[u];i;i=edge[i].next){ int to=edge[i].to; if(to==fa)continue; int val=get_wc(to,u,tot,wc); res+=val; mx=max(mx,val); } mx=max(mx,tot-res); if(mx<=tot/2)wc=u; return res; } int conp=0; int conq=0; int mp[3],zmp[3]; void get_dist(int u,int fa,int dist,int &conp){ if(st[u])return ; mp[dist%3]++; zmp[dist%3]++; for(int i=head[u];i;i=edge[i].next){ int to=edge[i].to; if(to==fa)continue; get_dist(to,u,dist+edge[i].dis,conp); } } int cal(int u){ if(st[u])return 0; get_wc(u,-1,get_size(u,-1),u); st[u]=true; int res=0; conq=0; for(int i=head[u];i;i=edge[i].next){ int to=edge[i].to; mp[0]=mp[1]=mp[2]=0; get_dist(to,u,edge[i].dis,conp); res-=(mp[1]*mp[2]); res-=(mp[0]*(mp[0]-1)*1ll)/2; res+=mp[0]; conp=0; } res+=(zmp[1]*zmp[2]); res+=(zmp[0]*(zmp[0]-1)*1ll)/2; zmp[0]=zmp[1]=zmp[2]=0; for(int i=head[u];i;i=edge[i].next)res+=cal(edge[i].to); return res; } int main(){ scanf("%d",&n); for(int i=1;i<=n-1;++i){ int x,y,w; scanf("%d%d%d",&x,&y,&w); edge_add(x,y,w); edge_add(y,x,w); } int fz=cal(1); fz=fz*2+n; int fm=n*n; int gcd=__gcd(fz,fm); if(fz==fm){ puts("1/1"); } else{ printf("%d/%d",fz/gcd,fm/gcd); } return 0; } /* 5 1 2 1 1 3 2 1 4 1 2 5 3 */