1. 1. 题目
  2. 2. 题解
    1. 2.1. 实现问题
  3. 3. AC代码

题目

公元 20442044 年,人类进入了宇宙纪元。

L 国有 nn 个星球,还有 n1n-1 条双向航道,每条航道建立在两个星球之间,这 n1n-1 条航道连通了 L 国的所有星球。

小 P 掌管一家物流公司, 该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 uiu_i 号星球沿最快的宇航路径飞行到 viv_i 号星球去。显然,飞船驶过一条航道是需要时间的,对于航道 jj,任意飞船驶过它所花费的时间为 tjt_j,并且任意两艘飞船之间不会产生任何干扰。

为了鼓励科技创新, L 国国王同意小 P 的物流公司参与 L 国的航道建设,即允许小 P 把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。

在虫洞的建设完成前小 P 的物流公司就预接了 mm 个运输计划。在虫洞建设完成后,这 mm 个运输计划会同时开始,所有飞船一起出发。当这 mm 个运输计划都完成时,小 P 的物流公司的阶段性工作就完成了。

如果小 P 可以自由选择将哪一条航道改造成虫洞, 试求出小 P 的物流公司完成阶段性工作所需要的最短时间是多少?
对于 100%100\% 的数据,保证:1ai,bin1 \leq a_i,b_i \leq n0ti10000 \leq t_i \leq 10001ui,vin1 \leq u_i,v_i \leq n

题解

首先提炼出题目要求——最大值最小 想到二分最大值
想一想二分之后如何判断是否合法
把大于 最大值 的路径都选出来,在树上标记出来
如果有一个边被选出来的所有路径都包括,且这其中最长的路径减去 边权小于 二分值,那么就合法,否则就不

实现问题

把大于 最大值 的路径都选出来

路径长度可以用树上前缀和

把大于 最大值 的路径都选出来,在树上标记出来

树上差分

出现的问题

1.二分写错
2.要用upper_bound
3.二分测试没全部初始化

AC代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
#include<bits/stdc++.h>
using namespace std;
const int N = 3e5 + 100;
int head[N], nxt[N << 1], edge[N << 1], to[N << 1], cnt = 1;

int point_to_edge[N];

int n, m;

int longest_path;
struct U
{
int m, n, length;

bool operator < (const U o) const
{
return length < o.length;
}
} u[N];


// <lca>

int anc[N][21], depth[N];

void lca_init()
{
for(int j = 1; j <= 20;j++)
{
for(int i = 1;i <= n;i++)
{
anc[i][j] = anc[anc[i][j - 1]][j - 1];
}
}
}

int lca_query(int x, int y)
{
if(depth[x] < depth[y])
swap(x, y);



for(int j = 20;j >= 0;j--)
{
if(depth[anc[x][j]] >= depth[y])
{
x = anc[x][j];
}
}

if(x == y) return x;
for(int j = 20;j >= 0;j--)
{
if(anc[x][j] != anc[y][j])
{
x = anc[x][j], y = anc[y][j];
}
}
return anc[x][0];
}
// </lca>

// <预处理前缀和+lca>

int sum[N];
void dfs_sum(int u, int fa)
{
depth[u] = depth[fa] + 1;
anc[u][0] = fa;
for(int i = head[u]; i; i = nxt[i])
{
int v = to[i];
if(v == fa) continue;
point_to_edge[v] = i;
sum[v] = sum[u] + edge[i];
dfs_sum(v, u);
}
}

// </预处理前缀和>


// <快读>
int read()
{
int f = 1, x = 0;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-') f = -1;
ch = getchar();
}

while(ch >= '0' && ch <= '9')
{
x = x * 10 + ch - '0';
ch = getchar();
}
return f * x;
}

// </快读>



void add(int x, int y, int z)
{
to[++cnt] = y;
edge[cnt] = z;
nxt[cnt] = head[x];
head[x] = cnt;
}

// <check>

// <树上差分>

int differ[N], sum_differ[N];
int best_can_edge_quan = 0;


void dfs_differ(int u, int fa, int T)
{
sum_differ[u] += differ[u];
for(int i = head[u]; i; i = nxt[i])
{
int v = to[i];
if(v == fa) continue;
dfs_differ(v, u, T);
sum_differ[u] += sum_differ[v];
}
if(sum_differ[u] == (m - T + 1) && best_can_edge_quan < edge[point_to_edge[u]])
{
best_can_edge_quan = edge[point_to_edge[u]];
}

// cout << "qwq "<<u <<" "<< sum_differ[u] << endl;
}

// </树上差分>

bool check(int T)
{
best_can_edge_quan = 0;
memset(differ, 0, sizeof(differ));
memset(sum_differ, 0, sizeof(sum_differ));


U temp;
temp.m = temp.n = 0;
temp.length = T;
int first_bad_order = upper_bound(u + 1, u + 1 + m/*!!!*/, temp) - u;
if(temp.length >= u[first_bad_order].length && first_bad_order == m) return 1;

// cout << "qwq"<<first_bad_order << endl;
// cout << first_bad_order << endl;
for(int i = first_bad_order;i <= m;i++)
{
differ[u[i].m]++, differ[u[i].n]++, differ[lca_query(u[i].n, u[i].m)] -= 2;
}

dfs_differ(1, 0, first_bad_order);


// cout << "qwq" << best_can_edge_quan << endl;
if(longest_path - best_can_edge_quan <= T)
{
return 1;
}
return 0;
}

// </check>


int binary_search(int r)
{
int l = 0;

while(l < r)
{
int mid = (l + r) >> 1;
// cout << mid << endl;
if(check(mid)/*可行*/)
{
r = mid;
// cout << mid << " ok" << endl;
}
else
{
l = mid + 1;
// cout << mid << " n" << endl;
}
}
return l;
}

int main()
{
n = read(), m = read();

int x, y, z;
for(int i = 1;i <= n - 1;i++)
{
x = read(), y = read(), z = read();
add(x, y, z), add(y, x, z);
}
dfs_sum(1, 0);
lca_init();



for(int i = 1;i <= m;i++)
{
u[i].m = read(), u[i].n = read(), u[i].length = sum[u[i].n] + sum[u[i].m] - 2 * sum[lca_query(u[i].n, u[i].m)];
longest_path = max(longest_path, u[i].length);
}

sort(u + 1, u + 1 + m);

printf("%d\n", binary_search(longest_path));

// differ[2] ++, differ[6] ++, differ[4] ++,differ[1] -= 2;
//
// dfs_differ(1, 0, 0);
// cout << sum_differ[1];
// cout << edge[point_to_edge[3]];
return 0;
}

其实树上差分合树上前缀和可以优化,不用递归