清华集训2016-汽水

清华集训2016-汽水

试题描述:
  牛牛来到了一个盛产汽水的国度旅行。

 这个国度的地图上有n个城市,这些城市之间用 n−1 条道路连接,任意两个城市之间,都存在一条路径连接。这些城市生产的汽水有许多不同的风味,在经过道路 i 时,牛牛会喝掉 wi 的汽水。牛牛非常喜欢喝汽水,但过量地用汽水是有害健康的,因此,他希望在他旅行的这段时间内,平均每天喝到的汽水的量尽可能地接近给定的一个正整数 k。

 同时,牛牛希望他的旅行计划尽可能地有趣,牛牛会先选择一个城市作为起点,然后每天通过一条道路,前往一个没有去过的城市,最终选择在某一个城市结束旅行。

 牛牛还要忙着去喝可乐,他希望你帮他设计出一个旅行计划,满足每天 |平均每天喝到的汽水−k|的值尽量小,请你告诉他这个最小值。

输入

第一行两个正整数 n,k。

接下来 n−1
行,每行三个正整数 ui,vi,wi,表示城市 ui 和城市 vi 之间有一条长度为 wi

的道路连接。

同一行相邻的两个整数均用一个空格隔开。

输出

一行一个整数,表示 |平均每天喝到的汽水−k| 的最小值的整数部分,即你只要将这个最小值向下取整然后输出即可。

样例输入

5 21
1 2 9
1 3 27
1 4 3
1 5 12

样例输出

1

solution:

简单的分数规划&点分治
不知道为什么考场上写的常数那么大,考完了又写了一遍跑得飞快(雾

看到最小化平均值,就应该尝试一下分数规划,也就是二分答案的做法
而因为要大规模处理链上的内容,应该要想到点分治
考虑这题怎么做
读入长度的时候先把所有长度减去k,这样最后求出来的一条链的长度的平均数就是答案
设点分治时候一棵子树的某一条链的有这样两个信息(A,B)
A表示该点到点分治的根的真实距离
B表示该点到点分治的根的深度
二分mid时,合并两条链(A1,B1) (A2,B2)有:

\[-mid < \frac{A1+A2}{B1+B2} < mid \]

因为要取整的原因这里用<,到后面输出答案直接减一即可
因为B1+B2始终大于零 所以考虑只A1+A2。
A1+A2>0时有 $$A1-B1mid < B2mid-A2$$ 发现对A排序之后可以单调维护后面一坨的最大或最小值
小于0同理,于是这题就oo了
复杂度:$$O(n\log n\log v)$$

#include<bits/stdc++.h>
#define ll long long
#define R register
#define inf_int 2003060400
#define inf_ll 1000000000000000000
using namespace std;
namespace IO
{
	template<class T>
	void rea(T &x)
	{
		char ch=getchar();int f(0);x = 0;
		while(!isdigit(ch)) {f|=ch=='-';ch=getchar();}
		while(isdigit(ch)) {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
		x = f?-x:x;
	}
	template<class T>
	T max(T a, T b) {return (a>b?a:b);}
	template<class T>
	T min(T a, T b) {return (a<b?a:b);}
}
using IO::rea;
const int N = 500005;
int n;
ll ans = inf_ll, k;
struct EDGE{int to, nex; ll w;}e[N];
int head[N], cnt;
void add(int x, int y, ll w)
{
	e[++cnt].to = y, e[cnt].nex = head[x];
	e[cnt].w = w, head[x] = cnt;
}
bool vis[N], flag;
int siz[N], root, Max, S;
namespace solveLink
{
	int tot;
	struct node
	{
		ll A, B; int anc;
		bool operator < (const node b) { return A < b.A; }
	} len[N];
	void getlen(int x, int fa, ll A, ll B, int anc)
	{
		len[++tot].A = A, len[tot].B = B, len[tot].anc = anc;
		for(R int i = head[x]; i; i = e[i].nex)
		{
			if(e[i].to == fa || vis[e[i].to]) continue;
			getlen(e[i].to, x, A+e[i].w, B+1, (x==fa?e[i].to:anc));
		}
	}
	bool update(node nod, ll B, node &a, node &b, ll fg, bool upd)
	{
		if(a.anc == nod.anc) if(fg*B*-1ll < fg*b.B) return 1; else;
		else if(fg*B*-1ll < fg*a.B) return 1; else;
		if(!upd) return 0;
		if(fg*B >= fg*a.B)
		{
			if(a.anc != nod.anc) b = a;
			a = nod; a.B = B;
		}
		else if(fg*B >= fg*b.B && nod.anc != a.anc) b = nod, b.B = B;
		return 0;
	}
	bool check1(ll x)
	{
		int p = tot;
		node mx, mx1; mx.A = mx1.A = mx.B = mx1.B = -inf_ll;
		for(R int i = 1; i <= tot && i < p; ++i)
		{
			while(len[p].A+len[i].A >= 0 && p > i)
				{ if(update(len[p], len[p].B*x-len[p].A, mx, mx1, 1, 1)) return 1; p--; }
			if(update(len[i], len[i].B*x-len[i].A, mx, mx1, 1, 0)) return 1;
		}
		return 0;
	}
	bool check2(ll x)
	{
		int p = 1;
		node mn, mn1; mn.A = mn1.A = mn.B = mn1.B = inf_ll;
		for(R int i = tot; i >= 1; --i)
		{
			while(len[p].A+len[i].A <= 0 && p < i)
				{ if(update(len[p], -len[p].A-len[p].B*x, mn, mn1, -1, 1)) return 1; p++; }
			if(update(len[i], -len[i].A-len[i].B*x, mn, mn1, -1, 0)) return 1;
		}
		return 0;
	}
	void Run(int x)
	{
		tot = 0; getlen(x, x, 0, 0, 0);
		sort(len+1, len+1+tot);
		ll l = 0, r = ans, mid;
		while(l < r)
		{
			mid = l+r >> 1;
			if(check1(mid) || check2(mid)) r = mid;
			else l = mid+1;
		}
		ans = min(ans, l);
	}
}
void Findrt(int x, int fa)
{
	siz[x] = 1;
	int maxson = 0;
	for(R int i = head[x]; i; i = e[i].nex)
	{
		if(vis[e[i].to] || e[i].to == fa) continue;
		Findrt(e[i].to, x);
		siz[x] += siz[e[i].to];
		maxson = max(maxson, siz[e[i].to]);
	}
	maxson = max(maxson, S-siz[x]);
	if(maxson < Max) Max = maxson, root = x;
}
void Run(int x)
{
	vis[x] = 1;
	solveLink::Run(x);
	int tots = S;
	for(R int i = head[x]; i; i = e[i].nex)
	{
		if(vis[e[i].to]) continue;
		if(siz[e[i].to] > siz[x]) S = tots-siz[x];
		else S = siz[e[i].to];
		Max = inf_int;
		Findrt(e[i].to, x);
		Run(root);
	}
}
void init()
{
	rea(n), rea(k);
	for(R int i = 1; i < n; ++i)
	{
		int x, y; ll w; rea(x), rea(y), rea(w);
		add(x, y, w-k), add(y, x, w-k);
	}
	S = n, Max = inf_int;
	Findrt(1, 0);
	Run(root);
	printf("%lld\n", ans-1);
}
int main()
{
	freopen("soda.in","r",stdin);
	freopen("soda.out","w",stdout);
	using IO::max;using IO::min; init();
	return 0;
}

posted @ 2020-02-22 20:38  Chopsticks  阅读(265)  评论(0编辑  收藏  举报