NOIP模拟赛2 B题 (点分治)

题目描述:给出一棵树,求出最小的 k k k,使得,且在树中存在路径 P P P,使得 k ≥ S k ≥ S kS k ≤ E k ≤ E kE. ( k k k为路径 P P P上的边的权值和)

很显然的点分治的题目。
按照点分治的套路,
1. 1. 1.先对这棵树进行重心划分
2. 2. 2.统计过重心的点的路径
3. 3. 3.递归求解

在统计路径的时候,排序后枚举加 l o w e r b o u n d lowerbound lowerbound,同时为避免统计到同一子树的(以当前划分的重心为根),可以每次统计一颗子树内的路径长度,就与已遍历子树进行答案统计,时间复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)
具体见代码

C o d e Code Code

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5, inf = 1 << 30;
struct w{
	int to, nx, s;
}head[MAXN + MAXN + 10];
int a[MAXN + 10], size[MAXN + 10], mx[MAXN + 10], dis[MAXN * 10 + 10], mn = inf;
int n, s, e, tot = 0;
bool vis[MAXN + 10];
inline int read();
inline void add(int, int, int, int);
void dfs(int, int, int);
int get_root(int, int, int);
void solve(int);
void calc(int);

int main(){
	freopen ("B.in","r",stdin);
	freopen ("B.out","w",stdout);
	n = read(), s = read(), e = read();
	for (register int i = 1; i < n; ++i){
		int x = read(), y = read(), z = read();
		add(x, y, i + i - 1, z);
		add(y, x, i + i, z);
	}
	dfs(1, 0, 0);
	solve(get_root(1, 0, n));
	if (mn == inf)	printf("-1\n");
	else    printf("%d\n", mn);
	return 0;
}

inline int read(){
	int x = 0;
	char c = getchar();
	while (!isdigit(c)) c = getchar();
	while (isdigit(c)) x = (x << 1) + (x << 3) + (c & 15), c = getchar();
	return x;
}

inline void add(int x, int y, int i, int z){head[i].to = y, head[i].nx = a[x], head[i].s = z, a[x] = i;}

void dfs(int x, int fa, int len){
	size[x] = 1, 	dis[++tot] = len;
	for (register int i = a[x]; i; i = head[i].nx){
		int v = head[i].to;
		if (v == fa || vis[v])	continue;
		dfs(v, x, len + head[i].s);
		size[x] += size[v];
	}
}

int get_root(int x, int fa, int num){
	int root = 0;
	mx[x] = num - size[x];
	for (register int i = a[x]; i; i = head[i].nx){
		int v = head[i].to;
		if (v == fa || vis[v])	continue;
		int tmp = get_root(v, x, num);
		if (!root || mx[tmp] < mx[root])	root = tmp;
		mx[x] = max(mx[x], size[v]);
	}
	if (!root || mx[root] > mx[x])	root = x;
	return root;
}

void solve(int x){
	vis[x] = 1;		tot = 0;
	for (register int i = a[x]; i; i = head[i].nx){
		int v = head[i].to, top = tot + 1;
		if (vis[v])	continue;
		dfs(v, x, head[i].s);
		calc(top);
	}
	for (register int i = a[x]; i; i = head[i].nx){
		int v = head[i].to;
		if (vis[v])	continue;
		solve(get_root(v, x, size[v]));
	}
}

void calc(int top){
	sort(dis + 1, dis + top);
	for (register int i = top; i <= tot; ++i){
		if (dis[i] >=s && dis[i] <= e)	mn = min(mn, dis[i]);
		int x = lower_bound(dis + 1, dis + top, s - dis[i]) - dis;
		if (x < top && dis[x] + dis[i] >= s && dis[x] + dis[i] <= e)	mn = min(mn, dis[x] + dis[i]);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值