1. 程式人生 > 實用技巧 >「gym - 102331J」Jiry Matchings

「gym - 102331J」Jiry Matchings

目錄


description

給定一棵帶權樹,對於 \(k\in[1, n - 1]\) 的每個 \(k\),輸出匹配數為 \(k\) 的最大權匹配的權和。

problem link。

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還有一道