點分治的一點理解
阿新 • • 發佈:2017-10-26
head == 今天 ace tro mes log scanf urn
今天菜狗我也學習了一波點分治。
下面是個板子
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 20010 using namespace std; int n,u,v,w,ans,sum,cnt,head[N],f[N],root,sz[N],t[5],d[N]; bool vis[N]; struct node{ int next,to,dis; }e[N*20]; void add(int u,int v,int w) { e[++cnt].next=head[u],e[cnt].to=v,e[cnt].dis=w,head[u]=cnt; } void getdeep(int u,int fa) { t[d[u]]++; for(int i=head[u];i;i=e[i].next) { int v=e[i].to; if(v==fa||vis[v])continue; d[v]=(d[u]+e[i].dis)%3; getdeep(v,u); } } int cal(int u,int v) { t[0]=t[1]=t[2]=0; d[u]=v; getdeep(u,0); return t[1]*t[2]*2+t[0]*t[0]; } void getroot(int u,int fa) { sz[u]=1,f[u]=0;//f數組記錄以u為根的最大子樹的大小,將最大子樹的頂點數最小的點當作分割頂點 for(int i=head[u];i;i=e[i].next) { int v=e[i].to; if(v==fa||vis[v])continue;//如果vis訪問過,表示這個點已經被刪掉了 getroot(v,u); sz[u]+=sz[v]; f[u]=max(f[u],sz[v]); } f[u]=max(f[u],sum-sz[u]);//以當前樹為時還要考慮其父親的子數大小 if(f[u]<f[root])root=u;//更新重心 } void solve(int u) { ans+=cal(u,0); vis[u]=1;//將當前點標記 for(int i=head[u];i;i=e[i].next) { int v=e[i].to; if(vis[v])continue; ans-=cal(v,e[i].dis); root=0;//初始化根 getroot(v,0);//找到聯通塊的重心 solve(root);//遞歸下一聯通塊 } } int gcd(int a,int b) { return !b?a:gcd(b,a%b); } int main() { scanf("%d",&n); for(int i=1;i<=n-1;i++)scanf("%d%d%d",&u,&v,&w),w%=3,add(u,v,w),add(v,u,w); sum=f[0]=n; getroot(1,0);//找樹的重心 solve(root);//點分治 int t=gcd(ans,n*n); printf("%d/%d\n",ans/t,n*n/t); return 0; }
點分治的一點理解