POJ1741 Tree 題解
題目大意
給一顆 個節點的樹,每條邊上有一個距離 。定義 爲 到 的最小距離。給定 值,求有多少點對 使 到 的距離小於等於 。
解題思路
如果用暴力枚舉,那麼時間複雜度爲
用DFS,時間複雜度爲
暴力膜不可取。
那麼我們重新分析,發現 有兩種情況(對於一整棵樹)
1. , 在同一棵子樹上
2. , 在不同子樹上
其中1的情況繼續分析,會變成2的情況,只要遞推下去就可以了。
那麼就會想到用點分治。
我們用 來求以x爲根節點,其中 中 數對的個數
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);
}
}
爲什麼要 ?因爲在 中 這對數已經被算進去了, 中 又算了一遍,所以要用 減一遍,至於爲什麼 ,那是因爲 數對可能原來計算時沒有算進去,當 時會被算進去,就會多減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;
}