【LOJ2952】「NOIP2018」赛道修建

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39972971/article/details/84983451

【题目链接】

【思路要点】

  • 首先,二分答案 a n s ans ,问题转化为判断是否存在 M M 条长度大于等于 a n s ans 的边不相交的路径,考虑树形 d p dp
  • d p i dp_i 为一个二元组 ( x , y ) (x,y) ,表示在点 i i 的子树中至多可以选出 x x 条路径,并且在选出 x x 条路径的基础上,根节点处还可以引出一条长度为 y y 的不与其余路径相交的路径。
  • 转移时我们需要考虑的问题入下:给定数组 ( a 1 , a 2 , . . . , a n ) (a_1,a_2,...,a_n) ,我们需要选出尽可能多的和大于等于 a n s ans 的数对,在最大化选出对数的情况下,最大化没有被选中的数的最小值。
  • 考虑如下贪心,对 a i a_i 排序,从小到大考虑每一个数 x x ,令当前数组中剩余的最大值为 M a x Max ,若 x + M a x < a n s x+Max<ans ,那么 x x 无法配对,其用途只能是留作没有被选中的数;否则,令当前数集中最小的满足 x + y a n s x+y≥ans y y ,将 x x y y 配对一定是最优的。
  • 该过程可以用栈来模拟。
  • 时间复杂度 O ( N L o g N L o g V ) O(NLogNLogV)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char ch = getchar();
	for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -f;
	for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
	x *= f;
}
struct info {int ans, res; };
struct edge {int dest, len; };
vector <edge> a[MAXN];
int n, m, mid; info dp[MAXN];
info operator + (info a, int b) {
	a.res += b;
	if (a.res >= mid) {
		a.ans++;
		a.res = 0;
	}
	return a;
}
int tot, len[MAXN], q[MAXN];
int top, fa[MAXN], s[MAXN];
void bfs(int from) {
	memset(fa, -1, sizeof(fa));
	int l = 1, r = 1;
	fa[1] = 0; q[1] = from;
	while (l <= r) {
		int pos = q[l++];
		for (unsigned i = 0; i < a[pos].size(); i++)
			if (fa[a[pos][i].dest] == -1) {
				fa[a[pos][i].dest] = pos;
				q[++r] = a[pos][i].dest; 
			}
	}
}
void work() {
	for (int p = n; p >= 1; p--) {
		int pos = q[p]; tot = 0;
		dp[pos] = (info) {0, 0};
		for (unsigned i = 0; i < a[pos].size(); i++)
			if (a[pos][i].dest != fa[pos]) {
				info tmp = dp[a[pos][i].dest] + a[pos][i].len;
				dp[pos].ans += tmp.ans, len[++tot] = tmp.res;
			}
		sort(len + 1, len + tot + 1);
		top = 0;
		for (int i = 1, j = tot + 1; i <= tot; i++) {
			while (j - 1 > i && len[i] + len[j - 1] >= mid) s[++top] = --j;
			if (i + 1 >= j) {
				s[++top] = i;
				dp[pos].ans += top / 2;
				if (top % 2 == 1) dp[pos].res = max(dp[pos].res, len[s[1]]);
				break;
			} else if (top != 0) top--, dp[pos].ans++;
			else dp[pos].res = max(dp[pos].res, len[i]);
		}
	}
}
int main() {
	freopen("track.in", "r", stdin);
	freopen("track.out", "w", stdout);
	read(n), read(m);
	for (int i = 1; i <= n - 1; i++) {
		int x, y, z; read(x), read(y), read(z);
		a[x].push_back((edge) {y, z});
		a[y].push_back((edge) {x, z});
	}
	bfs(1);
	int l = 1, r = 1e4 * n;
	while (l < r) {
		mid = (l + r + 1) / 2; work();
		if (dp[1].ans >= m) l = mid;
		else r = mid - 1;
	}
	printf("%d\n", l);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/84983451