POJ 1741 Tree(树的点分治)

时间:2020-12-20 04:24:37

题目链接:http://poj.org/problem?id=1741

题意:给出一棵树,定义dis(u,v)为两个节点之间的距离。dis(u,v)<=K时成(u,v)是合法的。问有多少个合法的顶点对(u,v)?

思路:(1)树的重心:删掉树的一个顶点,树将分成若干个子树。若有一个顶点,删掉后,使得子树节点的最大值最小,则该点称为重心。

(2)树的点分治:不知道标准的定义。我的理解就是找到一个点,删掉,然后递归的处理各个子树。这就是树的点分治的思想。

(3)对于本题,每次找到当前树的重心作为根节点。首先我们计算出通过根节点的合法顶点对个数,然后递归处理子树即可。首先,求出每个点到根节点的距离。然后将所有距离排序,则可以利用单调性O(n)计算出合法的顶点对。如下:

    sort(V1.begin(),V1.end());
    int L=0,R=SZ(V1)-1;
    while(L<R)
    {
        if(V1[L]+V1[R]<=m) ans+=R-L,L++;
        else R--;
    }

此时,有一些可能是只通过某个子树内的,因此还要先将其减去。

 




int n,m,ans;
vector<pair<int,int> > g[N];
int D[N],visit[N];
int s[N],Max[N];
vector<int> V1,V2;


int tot;


void DFS(int u,int pre)
{
    s[u]=1; Max[u]=0; tot++;
    int i,v;
    FOR0(i,SZ(g[u]))
    {
        v=g[u][i].first;
        if(v==pre||visit[v]) continue;
        DFS(v,u);
        s[u]+=s[v];
        if(s[v]>Max[u]) Max[u]=s[v];
    }
    V1.pb(Max[u]); V2.pb(u);
}


int getRoot(int u)
{
    V1.clear(); V2.clear(); tot=0;
    DFS(u,-1);
    int Min=INF,ans,i,temp;
    FOR0(i,SZ(V1))
    {
        temp=max(V1[i],tot-V1[i]);
        if(temp<Min) Min=temp,ans=V2[i];
    }
    return ans;
}


void getDeep(int u,int len,int pre)
{
    V1.pb(len);
    int i,v,w;
    FOR0(i,SZ(g[u]))
    {
        v=g[u][i].first;
        w=g[u][i].second;
        if(v==pre||visit[v]) continue;
        getDeep(v,len+w,u);
    }
}


void solve(int u)
{
    int root=getRoot(u);
    V1.clear();
    getDeep(root,0,-1);
    sort(V1.begin(),V1.end());
    int L=0,R=SZ(V1)-1;
    while(L<R)
    {
        if(V1[L]+V1[R]<=m) ans+=R-L,L++;
        else R--;
    }
    visit[root]=1;
    int i,v,w;
    FOR0(i,SZ(g[root]))
    {
        v=g[root][i].first;
        w=g[root][i].second;
        if(visit[v]) continue;
        V1.clear(); getDeep(v,w,root);
        sort(V1.begin(),V1.end());
        L=0,R=SZ(V1)-1;
        while(L<R)
        {
            if(V1[L]+V1[R]<=m) ans-=R-L,L++;
            else R--;
        }
    }
    FOR0(i,SZ(g[root]))
    {
        v=g[root][i].first;
        if(!visit[v]) solve(v);
    }
}


int main()
{
    Rush(n)
    {
        RD(m);
        if(!n&&!m) break;
        int i;
        FOR1(i,n) g[i].clear();
        clr(visit,0);
        int x,y,z;
        FOR1(i,n-1)
        {
            RD(x,y,z);
            g[x].pb(MP(y,z));
            g[y].pb(MP(x,z));
        }
        ans=0;  solve(1); PR(ans);
    }
}