POJ1741 Tree 題解

POJ1741 Tree 題解

原題鏈接:http://poj.org/problem?id=1741

題目大意

給一顆n 個節點的樹,每條邊上有一個距離v 。定義d(u,v)uv 的最小距離。給定k 值,求有多少點對uv 使uv 的距離小於等於k

解題思路

如果用暴力枚舉,那麼時間複雜度爲O(N3)
用DFS,時間複雜度爲O(N2)
暴力不可取。
那麼我們重新分析,發現(u,v) 有兩種情況(對於一整棵樹)
1.uv 在同一棵子樹上
2.uv 在不同子樹上
其中1的情況繼續分析,會變成2的情況,只要遞推下去就可以了。
那麼就會想到用點分治。
我們用Get(x,v) 來求以x爲根節點,其中d(x,fa[x])=v(u,v) 數對的個數

inline int Get(int x,int v){
    dis[x]=v;Sort[Num=1]=dis[x];
    dfs(x,0);int S=0; //dfs求該子樹上的節點到根節點的距離
    sort(Sort+1,Sort+1+Num);
    for (int i=1,j=Num;i<j;) if (Sort[i]+Sort[j]<=K) S+=j-i,i++;else j--;
    return S;
}
inline void Solve(int x){
    Ans+=Get(x,0);
    vis[x]=1;
    for (int i=lnk[x];i;i=nxt[i]){
        if (vis[son[i]]) continue;
        Ans-=Get(son[i],w[i]);
        Root=0;Sum=Size[son[i]];
        getrt(son[i],0);
        Solve(Root);
    }
}

樣例
爲什麼要Ans=Get(son[i],w[i]) ?因爲在Get(1,0)(3,5) 這對數已經被算進去了,Get(3,0)(3,5) 又算了一遍,所以要用Get(son[i],w[i]) 減一遍,至於爲什麼d(son[i],fa[son[i]])=w[i] ,那是因爲(u,v) 數對可能原來計算時沒有算進去,當Get(son[i],0) 時會被算進去,就會多減1,所以要加一道保險。

代碼

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int N,K,Ans,dis[10005];
int tot,nxt[20005],lnk[10005],son[20005],w[20005];
int Root,Sum,Num,Size[10005],F[10005],Sort[10005];
bool vis[10005];
inline void add(int x,int y,int z){son[++tot]=y;w[tot]=z;nxt[tot]=lnk[x];lnk[x]=tot;} //建邊
inline void getrt(int x,int fa){ //求重心
    F[x]=0;Size[x]=1;
    for (int i=lnk[x];i;i=nxt[i]){
        if (vis[son[i]]||son[i]==fa) continue;
        getrt(son[i],x);
        Size[x]+=Size[son[i]];
        F[x]=max(F[x],Size[son[i]]);
    }
    F[x]=max(F[x],Sum-Size[x]);
    if (F[x]<F[Root]) Root=x;
}
inline void dfs(int x,int fa){
    for (int i=lnk[x];i;i=nxt[i]){
        if (son[i]==fa||vis[son[i]]) continue;
        dis[son[i]]=dis[x]+w[i];
        Sort[++Num]=dis[son[i]];
        dfs(son[i],x);
    }
}
inline int Get(int x,int v){
    dis[x]=v;Sort[Num=1]=dis[x];
    dfs(x,0);int S=0;
    sort(Sort+1,Sort+1+Num);
    for (int i=1,j=Num;i<j;) if (Sort[i]+Sort[j]<=K) S+=j-i,i++;else j--;
    //如果d(i,j)<=k,那麼d(i+1,j),d(i+2,j),……,d(j-1,j)<=k,有(j-1-i+1)=j-i對數對
    return S;
}
inline void Solve(int x){
    Ans+=Get(x,0);
    vis[x]=1;
    for (int i=lnk[x];i;i=nxt[i]){
        if (vis[son[i]]) continue;
        Ans-=Get(son[i],w[i]);
        Root=0;Sum=Size[son[i]];
        getrt(son[i],0);
        Solve(Root);
    }
}
int main()
{
    while (1){
        scanf("%d%d",&N,&K);
        if (N==K&&N==0) return 0;
        tot=0;
        memset(vis,0,sizeof vis);
        memset(lnk,0,sizeof lnk);
        for (int i=1;i<N;i++){
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            add(x,y,z);add(y,x,z);
        }
        Root=0;Ans=0;
        F[0]=2e9;Sum=N;
        getrt(1,0);
        Solve(Root);
        printf("%d\n",Ans);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章