1. 程式人生 > 實用技巧 >2020 Multi-University Training Contest 2 1007 In Search of Gold

2020 Multi-University Training Contest 2 1007 In Search of Gold

題目連結:http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1007&cid=880

題意:給你一個n個點,n-1條邊的樹,每條邊的長度可能是a[i],也可能是b[i],有k條邊的長度是取a[i],n-k-1條邊的長度是取b[i]。然後要你求這棵樹可能的最小直徑。

思路:要求可能的最小直徑,可以想到二分數的直徑看是否滿足。二分看是否滿足時可以用樹型DP, f[i][j] 表示在 i 的子樹,其中有 j 條邊的的長度是取 a 陣列的所有樹直徑 ≤ mid 的方案中,與 i 點距離最遠的點到 i 的距離的最小

#include<bits/stdc++.h>
using
namespace std; typedef long long ll; int g[20005],v[40005],a[40005],b[40005],nxt[40005],siz[20005]; ll f[20005][25],h[20005]; int n,m; void dfs(int x,int y,ll mid) { siz[x]=f[x][0]=0; for(int i=g[x]; i; i=nxt[i]) { int u=v[i]; if(u==y) continue; dfs(u,x,mid); int A=a[i],B=b[i],pre=siz[x],cur=siz[u];
int now=min(pre+cur+1,m); for(int j=0; j<=now; j++) h[j]=mid+1; for(int j=0; j<=pre; j++) for(int k=0; k<=cur&&j+k<=m; k++) { if(f[x][j]+f[u][k]+A<=mid) h[j+k+1]=min(h[j+k+1],max(f[x][j],f[u][k]+A));
if(f[x][j]+f[u][k]+B<=mid) h[j+k]=min(h[j+k],max(f[x][j],f[u][k]+B)); } siz[x]=now; for(int j=0; j<=now; j++) f[x][j]=h[j]; } } int main() { int t; cin>>t; while(t--) { scanf("%d%d",&n,&m); ll l=0,r=0; for(int i=0; i<=n; i++) g[i]=0; int ed=0; for(int i=1; i<n; i++) { int x,y,A,B; scanf("%d%d%d%d",&x,&y,&A,&B); v[++ed]=y; a[ed]=A; b[ed]=B; nxt[ed]=g[x]; g[x]=ed; v[++ed]=x; a[ed]=A; b[ed]=B; nxt[ed]=g[y]; g[y]=ed; r+=max(A,B); } ll ans=0; while(l<=r) { ll mid=(l+r)/2; dfs(1,0,mid); if(f[1][m]<=mid) { ans=mid; r=mid-1; } else l=mid+1; } printf("%lld\n",ans); } }

可能值。然後轉移時合併之前已經 DP過的部分和新加入的子樹,如果兩部分到 i 的最長距離之和超過 mid 則這顆樹的直徑已經超過了mid,就不需要轉移了。