按慣例發題……
分析:
其實題目說那麼多,一句話就是:給定一棵帶權樹與條路徑,你可以使一條樹上的邊的權值變爲0,問你條路徑的長度的最大值最小是多少。
這道題讓我想到了貨車運輸這道題,但是更難,但方法可以借鑑。
因爲這是最大值最小問題,很顯然可以二分答案。
那這個二分判斷怎麼打呢?
我們如果遇到某條邊,所有超時的邊(即超過當前二分的答案)都經過此邊,且最長路徑減當前邊的權值小於等於二分的答案(即把這條邊的權值變爲0),就證明答案合法。
否則不合法。
那麼我們再往下分析,怎麼求某條邊被所有超時的邊經過?我們可以想到樹上差分!(關於樹上差分,以後會補更,這裏麻煩大家先自己去學,挺簡單的),把超時的路徑加一,最後跑一遍每一條邊,判斷用上文提到的方法。
我們又想,怎麼知道哪條邊超時了?沒錯,就是LCA!(LCA鏈接)(我們這裏就用倍增,不用樹鏈剖分了)
想必LCA求出來了,求距離也很容易了吧。
只要計算:(即當前距離等於點到根的距離加點到根的距離減去它們LCA到根的距離)
形象畫圖:
這裏稍微要注意一下,這裏的邊帶了權值,所以計算時不可以用深度,而是加入一個距離,的更新可以在LCA遞歸預處理時把當前的邊記錄,即賦值爲邊權。
從95到100
我發現許多人都卡在95分,被卡常,差幾毫秒超時了,這裏提供幾種優化:
1.用快讀(快讀鏈接)
2.在樹上差分時用序處理(雖然理論上是一樣的,但是循環的常數比遞歸的小)這裏可以在LCA遞歸預處理時記錄序。
代碼:
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 300010
using namespace std;
int n,m,x,y,z,MAX,l,r,mid,ans;
int a[N],b[N],lca[N],len[N],cf[N];
int f[N][21],dfn[N],dfn_tot,dep[N],dis[N],va[N],father[N];
int head[N],next[N*2],e[N*2],v[N*2],tot;//鏈式前向星
inline void read(int &x)//快讀加速
{
x=0;char c=getchar();
while (c<'0'||c>'9') c=getchar();
while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
}
inline int add(int x,int y,int z) {e[++tot]=y,v[tot]=z,next[tot]=head[x],head[x]=tot;}//鏈式前向星
inline void dfs(int x,int fa)//LCA遞歸預處理
{
dfn[++dfn_tot]=x/*dfs序*/,father[x]=fa,dep[x]=dep[fa]+1;
for (register int i=0;i<20;i++) f[x][i+1]=f[f[x][i]][i];
for (register int i=head[x];i;i=next[i])
if (e[i]!=fa) dis[e[i]]=dis[x]+v[i]/*dis數組更新*/,f[e[i]][0]=x,va[e[i]]=v[i],dfs(e[i],x);
}
int LCA(int x,int y)//求LCA
{
if (dep[x]<dep[y]) swap(x,y);
for (register int i=20;i>=0;i--)
{
if (dep[f[x][i]]>=dep[y]) x=f[x][i];
if (x==y) return x;
}
for (register int i=20;i>=0;i--)
if (f[x][i]&&f[y][i]&&f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return f[x][0];
}
inline int check(int mid)//二分判斷函數
{
memset(cf,0,sizeof(cf));
int k=0;
for (register int i = 1;i <= m; ++i)
if (len[i]>mid) cf[a[i]]++,cf[b[i]]++,cf[lca[i]]-=2,k++;//樹上差分
for (register int i=n;i;i--)//樹上差分dfs序優化
cf[father[dfn[i]]]+=cf[dfn[i]];
for (register int i=1;i<=n;i++)//枚舉邊判斷
if (cf[i]==k&&MAX-va[i]<=mid) return 1;
return 0;
}
int main()
{
freopen("transport.in","r",stdin);
freopen("transport.out","w",stdout);
read(n), read(m);
for (register int i=1;i<n;i++) read(x),read(y),read(z),add(x,y,z),add(y,x,z);
dfs(1,0);
for (register int i=1;i<=m;i++)
{
read(a[i]),read(b[i]);
lca[i]=LCA(a[i],b[i]),len[i]=dis[a[i]]+dis[b[i]]-2*dis[lca[i]]/*計算距離*/,MAX=max(MAX,len[i])/*求最大距離*/;
}
r=MAX;//二分
while (l<=r)
{
if (check(mid=l+r>>1)) r=mid-1,ans=mid;
else l=mid+1;
}
printf("%d",ans);
}