从一颗nn个节点的带权树中选出mm条互不相交的链,使得这mm条链中长度最小的链长度最大,求这个最大值。

m<n500000m < n\le 500000

Luogu P5021

Solution

显然二分答案,考虑check

显然对于每一个节点xxxx的儿子所剩的链能在xx处匹配就在xx处匹配,否则找出一条不能匹配的最长的链传到它的父亲上去。

匹配的过程用two pointers扫一下,再套一个二分找出在这个前提下能上传的最长链即可。

O(nlog2n)O(n\log^2 n)

Code

(考场代码,有点丑。。。)

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
#include <bits/stdc++.h>

#define x first
#define y second
#define y1 Y1
#define y2 Y2
#define mp make_pair
#define pb push_back

using namespace std;

inline void proc_status ()
{
ifstream t ("/proc/self/status");
cerr << string (istreambuf_iterator <char> (t), istreambuf_iterator <char> ()) << endl;
}

typedef long long LL;
typedef pair <int, int> pii;

template <typename T> inline int Chkmin (T &a, T b) {return a > b ? a = b, 1 : 0; }
template <typename T> inline int Chkmax (T &a, T b) {return a < b ? a = b, 1 : 0; }

inline int read ()
{
int sum = 0, fl = 1; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') fl = -1;
for (; isdigit(ch); ch = getchar()) sum = (sum << 3) + (sum << 1) + ch - '0';
return sum * fl;
}

const int Maxn = 50000 + 100;

int N, M;
int e, Begin[Maxn], To[Maxn << 1], Next[Maxn << 1], W[Maxn << 1];

inline void add_edge (int x, int y, int z)
{
To[++e] = y;
Next[e] = Begin[x];
Begin[x] = e;
W[e] = z;
}

int sum_w, Dp[Maxn], ans;
vector <int> vec[Maxn];

inline void dfs (int x, int f, int ans_now)
{
for (int i = Begin[x]; i; i = Next[i])
{
int y = To[i];
if (y == f) continue;
dfs(y, x, ans_now);
Dp[y] += W[i];
if (Dp[y] >= ans_now)
{
++ans;
Dp[y] = 0;
}
vec[x].pb(Dp[y]);
}
if (!vec[x].size()) return ;
sort(vec[x].begin(), vec[x].end());
int l = 0, r = (int)vec[x].size() - 1, res = 0;
int sum1 = 0, ans1 = 0;
while (r >= 0)
{
while (l < r && res + vec[x][r] < ans_now) Chkmax(sum1, res), res = vec[x][l], ++l;
if (res + vec[x][r] < ans_now) { Chkmax(sum1, vec[x][r]); break;}
res = 0; ++ans1; --r;
if (l > r) break ;
}

if (vec[x].size() == 1) { ans += ans1; Dp[x] = sum1; return ; }

int ll = 0, rr = vec[x].size() - 1, anss = sum1;
while (ll <= rr)
{
int mid = ll + rr >> 1;
int l2 = 0, r2 = (int)vec[x].size() - 1;
res = 0;
int tmp = vec[x][mid];
vec[x][mid] = 0;
int sum2 = 0, ans2 = 0;
if (l2 == mid) ++l2;
if (r2 == mid) --r2;
while (r2 >= 0)
{
while (l2 < r2 && (res + vec[x][r2] < ans_now || l2 == mid))
{
Chkmax(sum2, res);
if (l2 != mid) res = vec[x][l2];
++l2;
}
if (res + vec[x][r2] < ans_now) { Chkmax(sum2, vec[x][r2]); break;}
res = 0; ++ans2; --r2;
if (r2 == mid) --r2;
if (l2 > r2) break;
}
vec[x][mid] = tmp;
if (ans2 == ans1) ll = mid + 1, anss = tmp;
else rr = mid - 1;
}

ans += ans1, Dp[x] = anss;
}

inline int Check (int ans_now)
{
memset(Dp, 0, sizeof Dp);
for (int i = 1; i <= N; ++i) vec[i].clear();
ans = 0;
dfs(1, 0, ans_now);
return ans >= M;
}

inline void Solve ()
{
int l = 0, r = sum_w, Ans = 0;
while (l <= r)
{
int mid = l + r >> 1;
if (Check(mid)) Ans = mid, l = mid + 1;
else r = mid - 1;
}
cout<<Ans<<endl;
}

inline void Input ()
{
N = read(), M = read();
for (int i = 1; i < N; ++i)
{
int x = read(), y = read(), z = read();
add_edge (x, y, z);
add_edge (y, x, z);
sum_w += z;
}
}

int main()
{
#ifndef ONLINE_JUDGE
freopen("track.in", "r", stdin);
freopen("track.out", "w", stdout);
#endif
Input();
Solve();
return 0;
}