• 清华集训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(nlog nlog 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
    ", ans-1);
    }
    int main()
    {
    	freopen("soda.in","r",stdin);
    	freopen("soda.out","w",stdout);
    	using IO::max;using IO::min; init();
    	return 0;
    }
    
    
  • 相关阅读:
    函数地址经典基础C++笔试题(附答案)
    类型事务修改 mysql 表类型 实际测试可执行
    ARM7,ARM9有哪些区别
    I2C,SPI,UART总线的区别详解
    I²C
    AnyWhere——面向设备的编程模式
    Linux设备驱动编程之内存与I/O操作
    http://www.ibm.com/developerworks/cn/linux/lcnspidermonkey/index.html
    PCI Express总线接口板的设计与实现
    armfans文章搜集
  • 原文地址:https://www.cnblogs.com/heanda/p/12347042.html
Copyright © 2020-2023  润新知