題目傳送門
題意:
給一棵 個點的樹,每條邊有權。求一條簡單路徑,權值和等於 ,且邊的數量最小。輸出最小邊的數量。
數據範圍: 。
題解:
因爲 比較小,考慮開一個桶。我這個做法把數組改成 就超時了。
桶的含義:當前子樹中, 表示深度爲 的路徑的最小邊數。
表示當前子樹中,第 個點的深度。
表示當前子樹中,根節點到第 個點的邊數。
所以答案是 。
感受:
這道題用了很笨的做法寫了很久,看了答案,發現想複雜了,然後用更簡便的做法又做了一遍。
桶真的好用。
代碼:
#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const int maxn = 2e5 + 5 ;
const int maxm = 1e6 + 5 ;
int n , k ;
int num , head[maxn] ;
int root ;
int siz[maxn] ;
int vis[maxn] ;
int maxp[maxn] ;
int sum ;
int cur = 0 ;
int dis1[maxn] , dis2[maxn] ;
int bac[maxm] ;
struct Edge
{
int u , v , next , w ;
} edge[maxn << 1] ;
void add_edge(int u , int v , int w)
{
edge[num].v = v ;
edge[num].w = w ;
edge[num].next = head[u] ;
head[u] = num ++ ;
}
void get_root(int fa , int u)
{
siz[u] = 1 , maxp[u] = 0 ;
for(int i = head[u] ; i != -1 ; i = edge[i].next)
{
int v = edge[i].v ;
if(v == fa || vis[v]) continue ;
get_root(u , v) ;
siz[u] += siz[v] ;
maxp[u] = max(maxp[u] , siz[v]) ;
}
maxp[u] = max(maxp[u] , sum - siz[u]) ;
if(maxp[u] < maxp[root]) root = u ;
}
void get_dis(int fa , int u , int d , int cnt)
{
cur ++ ;
dis1[cur] = d , dis2[cur] = cnt ;
for(int i = head[u] ; i != -1 ; i = edge[i].next)
{
int v = edge[i].v , w = edge[i].w ;
if(v == fa || vis[v]) continue ;
get_dis(u , v , d + w , cnt + 1) ; //獲得d數組
}
}
int cal(int p , int d , int cnt)
{
int ans = 1e9 ;
int last = 0 ;
cur = 0 ;
bac[0] = 0 , dis1[0] = 0 , dis2[0] = cnt ;
for(int i = head[p] ; i != -1 ; i = edge[i].next)
{
int v = edge[i].v , w = edge[i].w ;
if(vis[v]) continue ;
get_dis(p , v , d + w , cnt + 1) ;
for(int i = last + 1 ; i <= cur ; i ++)
if(dis1[i] <= k)
ans = min(ans , bac[k - dis1[i]] + dis2[i]) ;
for(int i = last + 1 ; i <= cur ; i ++)
if(dis1[i] <= k)
bac[dis1[i]] = min(bac[dis1[i]] , dis2[i]) ;
last = cur ;
}
for(int i = 0 ; i <= cur ; i ++)
if(dis1[i] <= k)
bac[dis1[i]] = 1e9 ;
return ans ;
}
int solve(int p)
{
int ans = 1e9 ;
ans = min(ans , cal(p , 0 , 0)) ; //p子樹
vis[p] = 1 ; //刪除p節點
for(int i = head[p] ; i != -1 ; i = edge[i].next)
{
int v = edge[i].v , w = edge[i].w ;
if(vis[v]) continue ;
root = 0 , maxp[0] = 1e9 , sum = siz[v] ; //初始化
get_root(0 , v) ;
ans = min(ans , solve(root)) ;
}
return ans ;
}
int main()
{
scanf("%d%d" , &n , &k) ;
num = 0 , memset(head , -1 , sizeof(head)) ;
memset(vis , 0 , sizeof(vis)) ;
for(int i = 1 ; i <= n - 1 ; i ++)
{
int u , v , w ;
scanf("%d%d%d" , &u , &v , &w) ;
u ++ , v ++ ;
add_edge(u , v , w) , add_edge(v , u , w) ;
}
for(int i = 0 ; i <= 1e6 ; i ++) bac[i] = 1e9 ;
root = 0 , maxp[0] = 1e9 ; //初始化
sum = n ;
get_root(0 , 1) ; //最開始得到整棵樹的root
int x = solve(root) ;
if(x == 1e9) printf("-1\n") ;
else printf("%d\n" , x) ;
return 0 ;
}