POJ 1741 Tree | 樹分治
阿新 • • 發佈:2017-12-18
樹分治 等於 namespace poj edge i++ str static tree
求樹上距離小於等於K的點對對數
#include<cstdio> #include<algorithm> #include<cstring> #define N 10005 typedef long long ll; using namespace std; int n,K,fa[N],sze[N],son[N],dis[N],head[N]; int ecnt; bool vis[N]; ll ans; struct edge { int nxt,v,w; }e[2*N]; void add(int u,int v,int w)//加邊 { e[++ecnt].v=v,e[ecnt].w=w,e[ecnt].nxt=head[u],head[u]=ecnt; e[++ecnt].v=u,e[ecnt].w=w,e[ecnt].nxt=head[v],head[v]=ecnt; } void init()//初始化 { ans=ecnt=0; for (int i=1;i<=n;i++) head[i]=vis[i]=0; } int calcG(int sv)//計算重心 { static int qn,que[N]; int u,v,mx=n,G; que[qn=1]=sv,fa[sv]=0; for (int ql=1;ql<=qn;ql++)//以sv為根bfs求父子關系 { sze[u=que[ql]]=1,son[u]=0; for (int i=head[u];i;i=e[i].nxt) { if (vis[v=e[i].v] || v==fa[u]) continue; fa[v]=u,que[++qn]=v; } } for (int ql=qn;ql>=1;ql--)//bfs的逆序更新每個節點的最大子樹大小 { //son數組表示每個節點為根最大子樹大小 u=que[ql],v=fa[u]; if (qn-sze[u]>son[u]) son[u]=qn-sze[u];//如果他的子樹比qn/2要小,就更新成qn-sze if (son[u]<mx) G=u,mx=son[u];//更新重心 if (!v) break; sze[v]+=sze[u];//更新爸爸的子樹 if (sze[u]>son[v]) son[v]=sze[u]; } return G; } inline ll calc(int sv,int L) { static int qn,que[N],d[N]; int u,v,d_n=0; que[qn=1]=sv,dis[sv]=L,fa[sv]=0; for (int ql=1;ql<=qn;ql++) { d[d_n++]=dis[u=que[ql]]; for (int i=head[u];i;i=e[i].nxt) { if (vis[v=e[i].v] || v==fa[u]) continue ; fa[v]=u,dis[v]=dis[u]+e[i].w,que[++qn]=v; } } ll cnt=0; sort(d,d+d_n); int l=0,r=d_n-1; while (l<r) { if (d[l]+d[r]<=K) cnt+=r-l++; else --r; } return cnt; } void solve(int u) { int G=calcG(u);//求以u為根的子樹的重心 vis[G]=1; ans+=calc(G,0);//加上當前所在樹中的答案 if (!vis[e[i].v]) ans-=calc(e[i].v,e[i].w);//減去每個子樹的答案,因為子樹的答案在子樹中已經計算過了,不能重復計算 for (int i=head[G];i;i=e[i].nxt) if (!vis[e[i].v]) solve(e[i].v);//遞歸每個子樹 } int main() { while (scanf("%d%d",&n,&K),n || K) { init(); for (int i=1;i<n;i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); add(u,v,w); } solve(1);//以1為根 printf("%lld\n",ans); } return 0; }
POJ 1741 Tree | 樹分治