POJ-1741(樹分治)

樹的點分治

給出詳細的講解!!點這裏打開論文-分治算法在樹的路徑問題中的應用

本題目是他講的第一個例題;

個人理解:每次都找樹的重心,計算以重心爲根的子樹之間所貢獻的答案。不斷這樣下去;若是這棵樹是一條鏈,那麼就和快排,歸併的線性分治法同樣了。若是不是一條鏈那麼就至關於,選中一個點,標記爲使用過。而後樹會被這個節點劃分紅多棵子樹。而後這樣分治下去。思想好理解。可是代碼不是很好想!詳見註解。

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <math.h>

using namespace std;
typedef long long int LL;


const int maxn=20000+100;
struct Node 
{
    int to,val,next;
}edge[maxn];
int first[maxn],sz,ans,idx,root,n,k;
bool vis[maxn];

/* ----------------------*/
/**
 * 鄰接表部分
 */
void init()
{
    memset(first,-1,sizeof(first));
    memset(vis,0,sizeof(vis));
    sz=0;
}
void addedge(int s,int t,int val)
{
    edge[sz].val=val,edge[sz].to=t,edge[sz].next=first[s];
    first[s]=sz++;
}

/***** 
 * mi,用來找樹的重心的時候做比較用的;
 * mx[i]數組 表明i節點的子樹中最大的size;
 * size[i]表明i節點的子樹的節點數量;
 * dis[i]表明i好節點到根的深度;
 * ****************************/
int mi,mx[maxn],size[maxn],dis[maxn];




void dfssize(int x,int pre)  //x節點這個子樹求size,mx;
{
    size[x]=1;
    mx[x]=0;
    for(int i=first[x];i!=-1;i=edge[i].next)
    {
        int to=edge[i].to,val=edge[i].val;
        if(to!=pre&&!vis[to]) 
        {
            dfssize(to,x);
            size[x]+=size[to];
            if(size[to]>mx[x]) mx[x]=size[to];
        }
    }
}
void dfsroot(int rt,int x,int pre) //找rt這個 子樹的重心
{
    mx[x]=max(mx[x],size[rt]-size[x]);
    if(mx[x]<mi) mi=mx[x],root=x;
    for(int i=first[x];i!=-1;i=edge[i].next)
    {
        int to=edge[i].to;
        if(to!=pre&&!vis[to]) dfsroot(rt,to,x);
    }
}
void dfsdis(int x,int pre,int dd) //計算子樹 x的 dis 數組;
{
    dis[idx++]=dd;
    for(int i=first[x];i!=-1;i=edge[i].next)
    {
        int to=edge[i].to,val=edge[i].val;
        if(to!=pre&&!vis[to]) dfsdis(to,x,dd+val);
    }
}
/**
 * 計算 F(x)=(子樹x的 depth(i)+depth(j)<=k 的對數;)
 * calc(x,d);當d=0;返回的是x這顆子樹F(x),
 * 等d=某條邊權值,calc(x,d) 返回值表明子樹之間的F(x)
*/
int calc(int x,int d) 
{
    int res=0;
    idx=0;
    dfsdis(x,x,d);
    sort(dis,dis+idx);
    int i=0,j=idx-1;
    while(i<j)
    {
        while(dis[i]+dis[j]>k&&i<j) j--;
        res+=j-i;
        i++;
    }
    return res;
}
void dfs(int x)
{
    mi=n;
    dfssize(x,x);
    dfsroot(x,x,x);
    ans+=calc(root,0);//當前節點
    vis[root]=1;
    for(int i=first[root];i!=-1;i=edge[i].next)
    {
        int to=edge[i].to,val=edge[i].val;
        if(!vis[to])
        {
            ans-=calc(to,val);//減去當前節點的子樹之間的
//            printf(">> calc=%d\n",calc(root,val));
            dfs(to);
        }
    }
}
int main()
{
    while(scanf("%d%d",&n,&k)!=EOF)
    {
        if(n==0&&k==0) break;
        init();
        for(int i=1;i<n;i++)
        {
            int x,y,val;
            scanf("%d%d%d",&x,&y,&val);
            addedge(x,y,val);
            addedge(y,x,val);
        }
        ans=0;
        dfs(1);
        printf("%d\n",ans);
    }
    return 0;
}
相關文章
相關標籤/搜索