1. 程式人生 > >POJ 1741 Tree | 樹分治

POJ 1741 Tree | 樹分治

樹分治 等於 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 | 樹分治