「gym - 102331J」Jiry Matchings
阿新 • • 發佈:2020-07-31
目錄
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還有一道。