zoukankan      html  css  js  c++  java
  • [UOJ#276]【清华集训2016】汽水

    [UOJ#276]【清华集训2016】汽水

    试题描述

    牛牛来到了一个盛产汽水的国度旅行。

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

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

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

    输入

    第一行两个正整数 (n,k)

    接下来 (n−1) 行,每行三个正整数 (u_i,v_i,w_i),表示城市 (u_i) 和城市 (v_i) 之间有一条长度为 (w_i) 的道路连接。

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

    输出

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

    输入示例

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

    输出示例

    1
    

    数据规模及约定

    对于 (20 exttt{%}) 的数据,(n le 1000)

    对于另外 (20 exttt{%}) 的数据,保证编号为 (i(1 le i le n−1)) 的节点和编号为 (i+1) 的节点之间连接了一条边。

    对于另外 (20 exttt{%}) 的数据,保证数据是以 (1) 为根的完全二叉树(在完全二叉树中,节点 (i(2 le i le n)) 和节点 (lfloor i div 2 floor) 之间有一条道路)。

    对于另外 (20 exttt{%}) 的数据,保证除节点 (1) 以外,其他节点和节点 (1) 之间都有一条道路。

    对于 (100 exttt{%}) 的数据,(1 le n le 5 imes 10^4,0 le w_i le 10^{13},0 le k le 10^{13})

    题解

    我是垫底小王子!!!

    看到最小化平均值相关的东西,首先尝试分数规划(即二分答案)。

    假设当前二分的答案是 (x),那么就需要验证是否存在一条路径 (S) 使得 (|frac {sum_{i in S} {w_i}} {t} - k| < x)。展开绝对值得到 (-x < frac {sum_{i in S} {w_i}} {t} - k < x)

    下面令 (t = |S|)

    先看前半部分 (frac {sum_{i in S} {w_i}} {t} - k > -x),两边同乘 (t) 可以导出 (sum_{i in S} {w_i} > t(k - x)),然后移项(常规套路),得到 (sum_{i in S} {w_i - k + x} > 0)

    后半部分同理 (frac {sum_{i in S} {w_i}} {t} - k < x) (Rightarrow) (sum_{i in S} {w_i} < t(k + x)) (Rightarrow) (sum_{i in S} {w_i - k - x} < 0)

    然后因为是要查找存不存在这样的“链”,我们点分治。直接想跨重心的部分吧,将边权分别改成 (w_i - k - x)(w_i - k + x) 然后 dfs 出深度,令 (A_l) 表示某个已经处理过的子树中的节点的采用第一种边权的深度,(B_l) 已经处理过的表示采用第二种边权的深度,(A_r) 表示待处理的第一种边权深度,(B_r) 表示待处理的第二种边权深度,那么需要满足 (A_r < -A_l)(B_r > -B_l),这样我们可以将所有数对 ((-A_l, -B_l))(-A_l) 为关键字放到 Treap 中,维护 (-B_l) 的最小值即可,若所有关键字大于 (A_r) 的数对中 (-B_l) 的最小值小于 (B_r),则表示当前二分的答案可行。

    二分可以当前答案为上界,卡卡常数。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    using namespace std;
    #define LL long long
    
    LL read() {
    	LL x = 0, f = 1; char c = getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    	return x * f;
    }
    
    #define maxn 50010
    #define maxm 100010
    #define ool (1ll << 60)
    
    int n, m, head[maxn], nxt[maxm], to[maxm];
    LL K, val[maxm];
    
    void AddEdge(int a, int b, LL c) {
    	to[++m] = b; val[m] = c; nxt[m] = head[a]; head[a] = m;
    	swap(a, b);
    	to[++m] = b; val[m] = c; nxt[m] = head[a]; head[a] = m;
    	return ;
    }
    
    struct pll {
    	LL A, B;
    	pll() {}
    	pll(LL _, LL __): A(_), B(__) {}
    } ;
    struct Node {
    	LL A, B, mn; int r;
    	Node() {}
    	Node(LL _, LL __): A(_), B(__), r(rand()) {}
    	bool operator < (const Node& t) const { return A < t.A; }
    } ;
    struct Treap {
    	int rt, ToT, ch[maxn][2], fa[maxn];
    	Node ns[maxn];
    	
    	void Clear(int& o) {
    		if(!o) return ;
    		Clear(ch[o][0]); Clear(ch[o][1]);
    		fa[o] = 0; o = 0;
    		return ;
    	}
    	void clear() {
    		Clear(rt);
    		ToT = 0;
    		return ;
    	}
    	
    	void maintain(int o) {
    		ns[o].mn = ns[o].B;
    		if(ch[o][0]) ns[o].mn = min(ns[o].mn, ns[ch[o][0]].mn);
    		if(ch[o][1]) ns[o].mn = min(ns[o].mn, ns[ch[o][1]].mn);
    		return ;
    	}
    	
    	void rotate(int u) {
    		int y = fa[u], z = fa[y], l = 0, r = 1;
    		if(z) ch[z][ch[z][1]==y] = u;
    		if(ch[y][1] == u) swap(l, r);
    		fa[u] = z; fa[y] = u; fa[ch[u][r]] = y;
    		ch[y][l] = ch[u][r]; ch[u][r] = y;
    		maintain(y); maintain(u);
    		return ;
    	}
    	void insert(int& o, Node v) {
    		if(!o) {
    			ns[o = ++ToT] = v;
    			return maintain(o);
    		}
    		bool d = ns[o] < v;
    		insert(ch[o][d], v); fa[ch[o][d]] = o;
    		if(ns[ch[o][d]].r > ns[o].r) {
    			int t = ch[o][d];
    			rotate(t); o = t;
    		}
    		return maintain(o);
    	}
    	
    	LL qlarger(int o, LL lim, LL smaller_than_this) {
    		if(!o) return ool;
    		LL rmn = ch[o][1] ? ns[ch[o][1]].mn : ool;
    		if(ns[o].A <= lim) return qlarger(ch[o][1], lim, smaller_than_this);
    		else {
    			if(min(rmn, ns[o].B) < smaller_than_this) return smaller_than_this - 1;
    			return qlarger(ch[o][0], lim, smaller_than_this);
    		}
    	}
    } sol;
    
    int rt, size, f[maxn], siz[maxn];
    bool vis[maxn];
    void getrt(int u, int fa) {
    	siz[u] = 1; f[u] = 0;
    	for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa && !vis[to[e]]) {
    		getrt(to[e], u);
    		siz[u] += siz[to[e]];
    		f[u] = max(f[u], siz[to[e]]);
    	}
    	f[u] = max(f[u], size - siz[u]);
    	if(f[rt] > f[u]) rt = u;
    	return ;
    }
    pll now[maxn];
    int cnow;
    void dfs(int u, int fa, LL A, LL B, LL x) {
    	now[++cnow] = pll(A, B);
    	siz[u] = 1;
    	for(int e = head[u]; e; e = nxt[e]) if(!vis[to[e]] && to[e] != fa)
    		dfs(to[e], u, A + val[e] - K - x, B + val[e] - K + x, x), siz[u] += siz[to[e]];
    	return ;
    }
    bool check(int u, LL x) {
    	sol.clear();
    	for(int e = head[u]; e; e = nxt[e]) if(!vis[to[e]]) {
    		cnow = 0;
    		dfs(to[e], u, val[e] - K - x, val[e] - K + x, x);
    		for(int i = 1; i <= cnow; i++) {
    			if(now[i].A < 0 && now[i].B > 0) return 1;
    			if(now[i].B > sol.qlarger(sol.rt, now[i].A, now[i].B)) return 1;
    		}
    		for(int i = 1; i <= cnow; i++) sol.insert(sol.rt, Node(-now[i].A, -now[i].B));
    	}
    	return 0;
    }
    LL ans;
    void solve(int u) {
    	vis[u] = 1;
    	LL l = 0, r = ans;
    	while(l < r) {
    		LL mid = l + r >> 1;
    		if(check(u, mid)) r = mid; else l = mid + 1;
    	}
    	ans = l;
    	if(!ans) return ;
    	for(int e = head[u]; e; e = nxt[e]) if(!vis[to[e]]) {
    		f[rt = 0] = size = siz[to[e]]; getrt(to[e], u);
    		solve(rt);
    	}
    	return ;
    }
    
    int main() {
    	n = read(); K = read();
    	for(int i = 1; i < n; i++) {
    		int a = read(), b = read(); LL c = read();
    		AddEdge(a, b, c);
    	}
    	
    	ans = (LL)1e13 + 1;
    	f[rt = 0] = size = n; getrt(1, 0);
    	solve(rt);
    	
    	printf("%lld
    ", ans - 1);
    	
    	return 0;
    }
    
  • 相关阅读:
    git执行sudo git pull origin xxx 提示 AutoMatic merge failed;fix conflicts and then commit the result
    mysql 两表关联更新
    宝塔上的redis 性能调整的requirepass 密码与配置文件的 requirepass 不一致
    php 默认文档为index.htm 或者其他
    layerui 弹窗里出现下拉框select
    微信小程序文字超出显示省略号
    MySQL用存储过程创建日期字典表
    书单
    手动更新表记录时自动更新 UPDATE_DATE
    Nginx $proxy_add_x_forwarded_for 实现多租户判断
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/7668167.html
Copyright © 2011-2022 走看看