【APIO2021】封闭道路

  • 对于每个度数分别考虑,可以进行 dp,在状态中加入 \(0/1\) 表示是否保留与父亲的连边。

    转移:

    • \(f_{v,0} + w \le f_{v,1}\),则显然断掉更优。
    • 否则,将 \(f_{v,0} + w - f_{v, 1}\) 作为一条边压入堆中,每次选出最小的断掉,直到仅剩下 \(lim\) 条边。
  • 分析可得,若 \(deg_u \le lim\),则 \(u\) 的每条边是互不影响的,也就是 \(v\) 只用考虑边权 \(w\),而不关心 \(u\) 的子树内部情况。

    这样将整个树分成了若干独立的森林,维护每个结点当前独立边的集合 \(s,t\) 分别为放弃的与保留的,并维护放弃的权值和。

  • 每次只用考虑 \(deg_u > lim\) 的结点,这样总点数为 \(2n-2\)

    具体来说,然后对于每个结点,贪心保留前 \(lim\) 条边即可,这个可以用对顶堆来实现。

    而对于每条边作为独立边本身只会被压入,弹出 \(1\) 次,每次求解注意只访问 \(deg > lim\) 的结点。

  • 复杂度是均摊 \(O(n\log n)\) 的。

#include <bits/stdc++.h>
#include "roads.h"
#define fi first
#define se second
using namespace std;

typedef long long LL;

typedef pair <int, int> pii;

const int N = 3e5 + 10;

priority_queue <int> t[N];

priority_queue <int, vector <int>, greater <int> > s[N], q[N];

vector <pii> e[N];

int n, d[N], p[N], lim;

LL f[N][2], g[N], ans;

vector <LL> res;

bool vis[N];

bool cmp1_(pii u, pii v) {
	return d[u.fi] > d[v.fi];
}

bool cmp2_(int u, int v) {
	return d[u] > d[v];
}

void dfs_(int u) {
	f[u][0] = f[u][1] = 0, vis[u] = 1;
	
	while (s[u].size() > lim) {
		int w = s[u].top();
		g[u] += w, t[u].push(w);
		s[u].pop();
	}
	
	while (t[u].size() && lim - s[u].size() >= 1) {
		int w = t[u].top();
		s[u].push(w), g[u] -= w;
		t[u].pop();
	}
	
	f[u][0] = g[u];// 0 不保留, 1 保留 
	
	for (int v, w, i = 0; i < (int) e[u].size(); i++) {
		v = e[u][i].fi, w = e[u][i].se;
		
		if (d[v] <= lim)
			break;
		
		if (vis[v])
			continue;
		
		vis[v] = 1, dfs_(v), f[u][0] += min(f[v][0] + w, f[v][1]);
		
		if (f[v][0] + w <= f[v][1])  
			continue;
		
		q[u].push(f[v][0] + w - f[v][1]);
	}
	
	while (s[u].size() && q[u].size() && s[u].size() + q[u].size() > lim) {
		int w1 = s[u].top(), w2 = q[u].top();
		
		if (w1 >= w2) 
			f[u][0] += w2, q[u].pop();
		else	
			f[u][0] += w1, g[u] += w1, t[u].push(w1), s[u].pop();
	}
	
	while (s[u].size() > lim) {
		int w = s[u].top();
		f[u][0] += w, g[u] += w, t[u].push(w), s[u].pop();
	}
	
	while (q[u].size() > lim) {
		int w = q[u].top();	
		f[u][0] += w, q[u].pop();
	}
	
	f[u][1] = f[u][0];
	
	while (s[u].size() && q[u].size() && s[u].size() + q[u].size() >= lim) {
		int w1 = s[u].top(), w2 = q[u].top();
		
		if (w1 >= w2) 
			f[u][1] += w2, q[u].pop();
		else	
			f[u][1] += w1, g[u] += w1, t[u].push(w1), s[u].pop();
	}
	
	while (s[u].size() >= lim) {
		int w = s[u].top();
		f[u][1] += w, g[u] += w, t[u].push(w), s[u].pop();
	}
	
	while (q[u].size() >= lim) {
		int w = q[u].top();	
		f[u][1] += w, q[u].pop();
	}
	
	while (!q[u].empty())
		q[u].pop();
}

void del_(int u) {
	for (int v, w, i = 0; i < (int) e[u].size(); i++) {
		v = e[u][i].fi, w = e[u][i].se;
		
		if (d[v] <= lim)
			break;
		
		if (t[v].size() && t[v].top() > w)
			t[v].push(w), g[v] += w;
		else
			s[v].push(w); 
	}
}

vector <LL> minimum_closure_costs(int N, vector <int> U, vector <int> V, vector <int> W) {
	n = N;
	
	for (int u, v, w, i = 1; i < n; i++) {
		u = U[i - 1] + 1, v = V[i - 1] + 1, w = W[i - 1], ans += w;
		e[u].push_back(pii(v, w)), e[v].push_back(pii(u, w));
	}
	
	res.push_back(ans);
	
	for (int i = 1; i <= n; i++)
		d[i] = e[i].size(), p[i] = i;
	
	for (int i = 1; i <= n; i++) 
		sort(e[i].begin(), e[i].end(), cmp1_);
	
	sort(p + 1, p + n + 1, cmp2_);
	
	for (int i = 1, j = n; i < n; i++) {
		lim = i, ans = 0;
		
		while (j && d[p[j]] <= lim) 
			del_(p[j--]);
		
		for (int j = 1; j <= n; j++) {
			if (d[p[j]] <= lim) {
				for (int k = 1; k < j; k++)
					vis[p[k]] = 0;
				break;
			}
			
			if (!vis[p[j]])
				dfs_(p[j]), ans += f[p[j]][0];
		}
		
		res.push_back(ans);
	}
	
	return res;
}

【APIO2021】封闭道路

上一篇:利用.net 自带的工具生成webservice服务(windows)


下一篇:C#定时器的使用注意事项