码迷,mamicode.com
首页 > 其他好文 > 详细

「Luogu P4149」「IOI2011」Race

时间:2020-05-09 01:37:53      阅读:63      评论:0      收藏:0      [点我收藏+]

标签:lan   i++   ext   cond   const   memset   can   std   tar   

Description

给一棵 \(n\) 个点的树,每条边有权。求一条简单路径,权值和等于 \(k\),且边的数量最小。

Hint

  • \(1\le n\le 2\times 10^5\)
  • \(1\le k,\text{边权}\le 10^6\)

Solution

还行的一道点分治题。

\(k\) 的范围不是很大,所以开一个大小为 \(10^6\) 的桶,\(\text{rec}_i\) 表示权值和为 \(i\) 的路径的最小边数。

更具体地,假如当前重心(根)结点的所有子树的根分别为:\(x_1,x_2,\cdots,x_k\),而现在处理好了 \(x_1,x_2,\cdots,x_{k-1}\) 这几颗子树之间产生的答案,那么 \(\text{rec}\) 就保存了 \(x_1,x_2,\cdots,x_{k-1}\) 这几颗子树中所有结点到根路径的信息。

对于子树 \(x_k\) 中某一条路径,长度和权值分别为 \((L, V)\),那么这样更新答案:\(\text{ans}\leftarrow \min(\text{ans}, \text{rec}_{k-V}+L)\)

在处理完一颗子树之后,对于该子树中所有的路径,对 \(rec\) 这样更新:\(\text{rec}_V \leftarrow \min(\text{rec}_V, L)\)

于是……这道题就切掉了 QwQ!

  • 时间复杂度:\(O(n\log n)\)
  • 空间复杂度:\(O(n+k)\)

Code

#include <cstdio>
#include <cstring>
#include <utility>
#include <vector>

using namespace std;
const int N = 2e5 + 5;
const int K = 1e6 + 5;

int n, k, ans;
struct edge { int to, val; };
vector<edge> G[N];

int root;
int maxp[N], size[N];
bool centr[N];
int getSize(int x, int f) {
	size[x] = 1;
	for (auto y : G[x])
		if (y.to != f && !centr[y.to])
			size[x] += getSize(y.to, x);
	return size[x];
}
void getCentr(int x, int f, int t) {
	maxp[x] = 0;
	for (auto y : G[x]) {
		if (y.to == f || centr[y.to]) continue;
		getCentr(y.to, x, t);
		maxp[x] = max(maxp[x], size[y.to]);
	}
	maxp[x] = max(maxp[x], t - size[x]);
	if (maxp[x] < maxp[root]) root = x;
}

int tot;
pair<int, int> path[N];
vector<pair<int, int> > all;
int rec[K];
void getPaths(int x, int f, int l, int v) {
	if (v > k) return;
	path[++tot] = {l, v};
	for (auto y : G[x])
		if (y.to != f && !centr[y.to])
			getPaths(y.to, x, l + 1, v + y.val);
}

void solve(int x) {
	getSize(x, 0);
	maxp[root = 0] = N;
	getCentr(x, 0, size[x]);
	int s = root;
	centr[s] = true;
	
	for (auto y : G[s])
		if (!centr[y.to]) solve(y.to);
	
	rec[0] = 0;
	for (auto y : G[s]) {
		if (centr[y.to]) continue;
		tot = 0, getPaths(y.to, x, 1, y.val);
		for (register int i = 1; i <= tot; i++)
			ans = min(ans, rec[k - path[i].second] + path[i].first);
		for (register int i = 1; i <= tot; i++) {
			all.push_back(path[i]);
			rec[path[i].second] = min(path[i].first, rec[path[i].second]);
		}
	}
	
	for (auto i : all)
		rec[i.second] = 0x3f3f3f3f;
	all.clear();
	centr[s] = false;
}

signed main() {
	scanf("%d%d", &n, &k);
	for (register int i = 1; i < n; i++) {
		int s, t, v;
		scanf("%d%d%d", &s, &t, &v);
		s++, t++;
		G[s].push_back(edge{t, v});
		G[t].push_back(edge{s, v});
	}
	
	memset(rec, 0x3f, sizeof rec);
	ans = N, solve(1);
	printf("%d\n", ans == N ? -1 : ans);
	return 0;
}

「Luogu P4149」「IOI2011」Race

标签:lan   i++   ext   cond   const   memset   can   std   tar   

原文地址:https://www.cnblogs.com/-Wallace-/p/12853760.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!