[IOI2011]Race

[IOI2011]Race

题面描述

有一棵\(n\)个点 \(n-1\)条边的树 每条边都有一定的权值

求树上两点 使其的简单路径权值和为\(k\)且边数最少

输出为所用边数

具体详见原题

前置知识

点分治

注意事项

敲黑板
image

俺因为这个挂了有一会

思路

裸的点分治

一般你可以用点分治

但是注意到有边的限制(毕竟是IOI

于是在看到\(k \leq 10^6\)后果断开桶 空间换时间

加上本身自带的减小常数buff 跑得飞快

image

具体开桶的话就是拿一个\(Edge\)数组记录所用边数

\(Edge[i]\)就是第i个更新到的所用的边数 跟dis组成一对连体婴儿

在记\(min[i]\)表示长度为i最少要用多长的边

那么我们可以得到式子

\(ans = \min_{i=1}^{tot}min[k - dis[i]] + Edge[i]\)

\(min[i] = \min_{i=1}^{tot}Edge[i]\)

然后每次做完记得赋初值 即\(\infty\)

Tips

别像我一样喜欢压行结果调了2天不到一点

错误示范

for (auto v : G[u]) if(!delete[son = v.first]) {
	...
}

还有的就是因为 \(dis\) > \(k\) 直接return就行了

这样的只用判一下ans是否 >n就好了

记得赋初值

int ans = MAX_INT;

Code

#include <bits/stdc++.h>

using namespace std;

typedef pair<int,int> PII;

int read(int x = 0, bool f = false, char ch = getchar()) {
	for (; !isdigit(ch); ch = getchar()) f |= (ch == '-');
	for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
	return f ? ~x + 1 : x;
}

const int N = 2e5 + 5, M = 1e6 + 5;

int n, k, root, son, ans = INT_MAX, tot, SIZE, u, v, w;
int mx[N], siz[N], anc[N], mn[M], dis[N], Edge[N];
bool del[N];
vector<PII> G[N];

#define x first
#define y second

void find(int u, int fa) {
	siz[u] = 1; mx[u] = 0;
	for (auto v : G[u]) {
		int son = v.x;
		if (del[son] || son == fa) continue;
		find(son, u); siz[u] += siz[son];
    	mx[u] = max(mx[u], siz[son]);
	}
	mx[u] = max(mx[u], SIZE - siz[u]);
	if (mx[u] < mx[root]) root = u;
}

void dfs(int u, int fa, int p, int dep) {
	if (p > k) return;
	dis[++ tot] = p; Edge[tot] = dep;
	for (auto v : G[u]) {
		int son = v.x;
		if (del[son = v.x] || son == fa) continue;
		dfs(son, u, p + v.y, dep + 1);
	}
}

void calc(int u) {
	mn[0] = tot = 0;
	for (auto v : G[u]) {
		int son = v.x;
		if (del[son]) continue;
		int cnt = tot;
		dfs(son, u, v.y, 1);
		for (int i = cnt + 1; i <= tot; ++i) 
			ans = min(ans, mn[k - dis[i]] + Edge[i]);
		for (int i = cnt + 1; i <= tot; ++i) 
			mn[dis[i]] = min(mn[dis[i]], Edge[i]);
	}
	for (int i = 1; i <= tot; ++i) mn[dis[i]] = 1e9;
}
	
void solve(int u) {
	del[u] = true; calc(u);
	for (auto v : G[u]) {
		int son = v.x;
		if (del[son]) continue;
		root = 0; SIZE = siz[son]; find(son, u); solve(root);
	}
}

signed main(int argc, char *argv[]) {
	n = read(), k = read();
	for (int i = 1; i <= n - 1; ++i)
		u = read() + 1, v = read() + 1, w = read(),
		G[u].push_back(make_pair(v,w)),
		G[v].push_back(make_pair(u,w));
	mx[root = 0] = (SIZE = n) + 1;
	memset(mn, 0x3f, sizeof mn); 
	SIZE = n, find(1, 0); solve(root);	
	return printf("%d\n", ans >= n ? -1 : ans), 0;
}
posted @ 2021-08-22 15:37  xxcxu  阅读(121)  评论(0编辑  收藏  举报