NOIP 2015 运输计划
NOIP 2015 运输计划
题目描述
给定一棵树,树有边权。现有\(m\)条路径,求把一条边权变为0后,最大的路径长度的最小值。
解题思路
首先考虑\(O(nm)\)的暴力,枚举每条边,然后暴力check一下最长的路径。
思考怎么优化这个暴力。一个很明显的地方是,如果我们不把最长路径上的某条边变为0,那么答案不会变化。这样我们要枚举最长路径上的边,删去之后找最大值。
经过这条边的路径的最大值肯定就是枚举的路径长度-边长。考虑求不经过的。求不经过某条边的路径长度最大值。
一条树上路径可以树链剖分,在树上被剖成log个dfs序区间\([l_1,r_1] [l_2,r_2]...[l_k,r_k]\)。没被这个路径覆盖的边:\([1,l_1 -1], [r_1 + 1, l_2 - 1]...[r_k+1, n]\). 区间总数还是\(O(logn)\)个的。对每一条路径这样再用线段树维护一下,复杂度\(O(mlog^2n)\)。
不用数据结构的求法:\(e= (u, v)\)。当u是父亲,不经过e的路径在v的子树里。剩余的路径:把树倒过来,当v是父亲,u的子树的路径。从最长路径的两个端点做dp,求子树最长路径。
注意\(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;
}