「gym - 102331J」Jiry Matchings
description
给定一棵带权树,对于 \(k\in[1, n - 1]\) 的每个 \(k\),输出匹配数为 \(k\) 的最大权匹配的权和。
solution
记 \(f_{0/1,x,i}\) 表示在 \(x\) 的子树中选 \(i\) 条匹配,不选中/选中 \(x\) 的最大和。
不难想到 \(f\) 是凸的(根据费用流的性质),因此合并两个 \(f\) 的过程可以做到线性。
如果在链上,可以分治做到 \(O(n\log n)\)。
如果在树上,可以进行链分治:将树轻重链剖分,以重链为单位自底向上合并。具体来说你需要做两步:
(1)对于每个点,合并它所有轻儿子的答案,可以分治;
(2)对于每条重链,套用链的分治方法求出答案。
总复杂度 \(O(n\log^2n)\)。
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef vector<ll> vl;
const int N = 200000;
const ll INF = (1ll << 60);
struct edge{
int to, dis; edge *nxt;
}edges[2*N + 5], *adj[N + 5], *ecnt = edges;
void adde(int u, int v, int w) {
edge *p = (++ecnt), *q = (++ecnt);
(*p) = (edge){v, w, adj[u]}, adj[u] = p;
(*q) = (edge){u, w, adj[v]}, adj[v] = q;
}
#define repG(p, x) for(edge *p=adj[x];p;p=p->nxt)
int fa[N + 5], siz[N + 5], hvy[N + 5], hd[N + 5];
void dfs(int x, int f) {
siz[x] = 1, hvy[x] = 0, fa[x] = f;
repG(p, x) if( p->to != f ) {
dfs(p->to, x), siz[x] += siz[p->to];
if( siz[hvy[x]] < siz[p->to] )
hvy[x] = p->to, hd[x] = p->dis;
}
}
vl merge(const vl &a, const vl &b) {
if( a.empty() ) return b;
if( b.empty() ) return a;
int na = a.size(), nb = b.size(); vl c(na + nb - 1, -INF);
int pa = 1, pb = 1, pc = 1; c[0] = a[0] + b[0];
while( pa < na && pb < nb ) {
if( a[pa] - a[pa - 1] > b[pb] - b[pb - 1] )
c[pc] = c[pc - 1] + a[pa] - a[pa - 1], pa++, pc++;
else c[pc] = c[pc - 1] + b[pb] - b[pb - 1], pb++, pc++;
}
while( pa < na ) c[pc] = c[pc - 1] + a[pa] - a[pa - 1], pa++, pc++;
while( pb < nb ) c[pc] = c[pc - 1] + b[pb] - b[pb - 1], pb++, pc++;
return c;
}
vl add(const vl &a, const vl &b) {
int na = a.size(), nb = b.size(); vl c(max(na, nb), -INF);
for(int i=0;i<na;i++) c[i] = max(c[i], a[i]);
for(int i=0;i<nb;i++) c[i] = max(c[i], b[i]);
return c;
}
vl move(const vl &a, const int &d) {
int na = a.size(); vl c(na + 1, -INF);
for(int i=0;i<na;i++) c[i + 1] = max(c[i + 1], a[i] + d);
return c;
}
vl f[2][N + 5], g[2][N + 5], h[2][2][N + 5]; int t[N + 5], cnt;
void get1(vl &v0, vl &v1, int d) {
cnt++, g[0][cnt] = add(v0, v1), g[1][cnt] = move(v0, d);
v0.clear(), v1.clear();
}
void clear1(int x) {
g[0][x].clear(), g[1][x].clear();
}
void solve1(int l, int r) {
if( l == r ) return ;
int m = (l + r) >> 1;
solve1(l, m), solve1(m + 1, r);
vl a = merge(g[0][l], g[0][m + 1]);
vl b = merge(g[0][l], g[1][m + 1]);
vl c = merge(g[1][l], g[0][m + 1]);
g[0][l] = a, g[1][l] = add(b, c), clear1(m + 1);
}
void get2(vl &v0, vl &v1) {
cnt++, h[0][1][cnt].clear(), h[1][0][cnt].clear(), h[0][0][cnt] = v0, h[1][1][cnt] = v1;
v0.clear(), v1.clear();
}
void clear2(int x) {
h[0][0][x].clear(), h[0][1][x].clear(), h[1][0][x].clear(), h[1][1][x].clear();
}
void solve2(int l, int r) {
if( l == r ) return ;
int m = (l + r) >> 1;
solve2(l, m), solve2(m + 1, r);
if( l + 1 == r ) {
vl tp = move(merge(h[0][0][l], h[0][0][m + 1]), t[m]);
for(int o1=0;o1<=1;o1++) {
vl to1 = h[o1][o1][l];
for(int o2=0;o2<=1;o2++)
h[o1][o2][l] = merge(to1, h[o2][o2][m + 1]);
}
h[1][1][l] = add(h[1][1][l], tp);
}
else if( l + 2 == r ) {
vl tp0 = move(merge(h[0][0][l], h[0][0][m + 1]), t[m]);
vl tp1 = move(merge(h[1][0][l], h[0][0][m + 1]), t[m]);
for(int o1=0;o1<=1;o1++) {
vl t0 = h[o1][0][l], t1 = h[o1][1][l];
for(int o2=0;o2<=1;o2++)
h[o1][o2][l] = merge(add(t0, t1), h[o2][o2][m + 1]);
}
h[0][1][l] = add(h[0][1][l], tp0), h[1][1][l] = add(h[1][1][l], tp1);
}
else {
for(int o1=0;o1<=1;o1++) {
vl t0 = h[o1][0][l], t1 = h[o1][1][l];
for(int o2=0;o2<=1;o2++) {
vl a = merge(t0, h[0][o2][m + 1]);
vl b = merge(add(t0, t1), add(h[0][o2][m + 1], h[1][o2][m + 1]));
h[o1][o2][l] = add(move(a, t[m]), b);
}
}
}
clear2(m + 1);
}
void dfs2(int x, int tp) {
if( !hvy[x] )
f[0][x] = vl(1, 0), f[1][x] = vl(1, 0);
else {
repG(p, x) if( p->to != fa[x] && p->to != hvy[x] )
dfs2(p->to, p->to);
dfs2(hvy[x], tp), cnt = 0;
repG(p, x) if( p->to != fa[x] && p->to != hvy[x] )
get1(f[0][p->to], f[1][p->to], p->dis);
if( cnt == 0 ) f[0][x] = vl(1, 0), f[1][x] = vl(1, 0);
else solve1(1, cnt), f[0][x] = g[0][1], f[1][x] = g[1][1], clear1(1);
}
if( x == tp ) {
cnt = 0; for(int i=x;i;i=hvy[i]) get2(f[0][i], f[1][i]), t[cnt] = hd[i];
solve2(1, cnt), f[0][x] = add(h[0][0][1], h[0][1][1]), f[1][x] = add(h[1][0][1], h[1][1][1]), clear2(1);
}
}
int main() {
// freopen("data.in", "r", stdin);
int n; scanf("%d", &n);
for(int i=1,u,v,w;i<n;i++)
scanf("%d%d%d", &u, &v, &w), adde(u, v, w);
dfs(1, 0), dfs2(1, 1);
vl v = add(f[0][1], f[1][1]); f[0][1].clear(), f[1][1].clear();
for(int i=1;i<n;i++) {
if( i < (int)v.size() ) printf("%lld", v[i]);
else putchar('?');
putchar(i + 1 == n ? '\n' : ' ');
}
}
details
合理使用 vector 可以简化代码难度。
类似的题隔壁loj还有一道。
update in ????/??/??:我总感觉这道题可以使用 top tree 做到 \(O(n\log n)\),奈何我并不会 top tree。