NOIP 2015 運輸計劃
阿新 • • 發佈:2020-08-06
NOIP 2015 運輸計劃
題目描述
給定一棵樹,樹右邊權。現有\(m\)條路徑,求把一條邊權變為0後,最大的路徑長度的最小值。
解題思路
首先考慮\(O(nm)\)的暴力,列舉每條邊,然後暴力check一下最長的路徑。
思考怎麼優化這個暴力。一個很明顯的地方是,如果我們不把最長路徑上的某條邊變為0,那麼答案不會變化。這樣我們要列舉最長路徑上的邊,刪去之後找最大值。
經過這條邊的路徑的最大值肯定就是列舉的路徑長度-邊長。考慮求不經過的。
一條樹上路徑可以樹鏈剖分,在樹上被剖成log個dfs序區間。這包含所有經過的路徑。我們把一個點的父邊放在該點上,這樣我們對這log個區間求個補集,就是沒有經過的邊的區間,總數還是\(O(logn)\)
注意\(u=v\)的路徑需要特判。
#include<bits/stdc++.h> using namespace std; const int N = 3e5 + 11; int n, m; int head[N], nex[N<<1], to[N<<1], wei[N<<1], size; int dis[N], dep[N], fa[N]; int son[N], sz[N], dfn[N], top[N], cnt; struct Path{ int u, v, w; }E[N]; struct seg{ int l, r; bool operator < (const seg &a)const{ return l < a.l; } }a[N], b[N]; struct Seg{ #define ls (p << 1) #define rs (p << 1 | 1) int t[N<<2], tag[N<<2]; void add_tag(int p, int v){ t[p] = max(t[p], v); tag[p] = max(tag[p], v); } void push_down(int p){ if(!tag[p])return ; add_tag(ls, tag[p]); add_tag(rs, tag[p]); tag[p] = 0; } void modi(int p, int l, int r, int x, int y, int v){ if(l > y || r < x)return ; if(l >= x && r <= y){ add_tag(p, v); return ; } push_down(p); int mid = l + r >> 1; modi(ls, l, mid, x, y, v); modi(rs, mid + 1, r, x, y, v); t[p] = max(t[ls], t[rs]); } int query(int p, int l, int r, int x){ if(l == r){ return t[p]; } int mid = l + r >> 1; push_down(p); if(mid >= x)return query(ls, l, mid, x); else return query(rs, mid + 1, r, x); } }T; int read(){ int x = 0; char ch = getchar(); while(ch > '9' || ch < '0')ch = getchar(); while(ch >= '0' && ch <= '9')x = 10 * x + ch - 48, ch = getchar(); return x; } bool cmp(Path a, Path b){ return a.w > b.w; } void add(int x, int y, int z){ to[++size] = y; nex[size] = head[x]; head[x] = size; wei[size] = z; } void dfs(int u){ sz[u] = 1; dep[u] = dep[fa[u]] + 1; for(int i = head[u];i;i = nex[i]){ int v = to[i]; if(v == fa[u])continue; fa[v] = u; dis[v] = dis[u] + wei[i]; dfs(v); sz[u] += sz[v]; if(sz[v] > sz[son[u]])son[u] = v; } } void dfs(int u, int tp){ top[u] = tp; dfn[u] = ++cnt; if(son[u])dfs(son[u], tp); for(int i = head[u];i;i = nex[i]){ int v = to[i]; if(v == fa[u] || v == son[u])continue; dfs(v, v); } } int lca(int u, int v){ while(top[u] != top[v]){ if(dep[top[u]] < dep[top[v]])swap(u, v); u = fa[top[u]]; } if(dep[u] > dep[v])return v; else return u; } void get_cover(int u, int v, int w){ int cnta = 0, cntb = 0; while(top[u] != top[v]){ if(dep[top[u]] < dep[top[v]])swap(u, v); a[++cnta] = (seg){dfn[top[u]], dfn[u]}; u = fa[top[u]]; } if(u != v){ if(dep[u] > dep[v])swap(u, v); a[++cnta] = (seg){dfn[u] + 1, dfn[v]}; } sort(a + 1, a + 1 + cnta); int p = 2; for(int i = 1;i <= cnta; i++){ if(a[i].l != p){ b[++cntb] = (seg){p, a[i].l - 1}; } p = a[i].r + 1; } if(p <= n)b[++cntb] = (seg){p, n}; for(int i = 1;i <= cntb; i++){ T.modi(1, 2, n, b[i].l, b[i].r, w); } } int main(){ cin>>n>>m; int u, v, w; for(int i = 1;i < n; i++){ u = read(); v = read(); w = read(); add(u, v, w); add(v, u, w); } dfs(1); dfs(1, 1); for(int i = 1;i <= m; i++){ u = read(); v = read(); w = dis[u] + dis[v] - 2 * dis[lca(u, v)]; E[i] = (Path){u, v, w}; //printf("i=%d u=%d v=%d w=%d\n", i, u, v, w); get_cover(u, v, w); } sort(E + 1, E + 1 + m, cmp); u = E[1].u, v = E[1].v; int ans = 1e9; if(u == v)ans = 0; while(u != v){ if(dep[v] > dep[u])swap(u, v); ans = min(ans, max(E[1].w - (u == v ? 0 : (dis[u] - dis[fa[u]])), T.query(1, 2, n, dfn[u]))); u = fa[u]; } cout<<ans<<endl; return 0; }