UOJ#276. 【清华集训2016】汽水 二分答案 点分治
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ276.html
题解
首先,读入的时候就将所有的 $w_i$ 减掉 $k$ 。
于是我们要求的就是平均值最接近 0 的。
直接点分治,然后得到一些一端为当前点分中心的路径,设 $a,b$ 为其中两条路径,设 $v_a,v_b$ 为路径的边权和,$t_a,t_b$ 为路径的边数。
二分一个答案,假设差别**小于** $A$。由于题目要求的是下取整,所以我们为了方便,设的是**小于** $A$ ,这样做,最终只需要把答案减一就好了。
那么,如果合并路径 $a,b$ 可以满足条件,那么就会满足:
$$\left|\cfrac{v_a+v_b}{t_a+t_b}\right|<A\\|v_a+v_b|<A(t_a+t_b)\\=\begin{cases}v_a-At_a+v_b-At_b<0\ \ \ \ \ \ (v_a+v_b\geq 0)\\v_a+At_a+v_b+At_b>0\ \ \ \ \ \ (v_a+v_b<0)\end{cases}$$
也就是说,我们只需要对于正的 $v_a$ 和负的 $v_a$ 分开考虑,在保证取到右侧条件的基础上,维护一下最大最小值之类的东西就好了。
具体还是看代码吧。
代码
#include <bits/stdc++.h>
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long LL;
LL read(){
LL x=0;
char ch=getchar();
while (!isdigit(ch))
ch=getchar();
while (isdigit(ch))
x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return x;
}
const int N=50005;
const LL INF=1LL<<60;
int n;
LL K,ans=INF;
vector <pair <int,LL> > e[N];
int vis[N],size[N],Maxsize[N],root,Size;
void get_root(int x,int pre){
size[x]=1,Maxsize[x]=0;
for (auto E : e[x])
if (E.fi!=pre&&!vis[E.fi]){
get_root(E.fi,x);
size[x]+=size[E.fi];
Maxsize[x]=max(Maxsize[x],size[E.fi]);
}
Maxsize[x]=max(Maxsize[x],Size-size[x]);
if (Maxsize[x]<Maxsize[root])
root=x;
}
struct Node{
int t,id;
LL v;
Node(int _t=0,LL _v=0,int _id=0){
t=_t,v=_v,id=_id;
}
friend bool operator < (Node a,Node b){
return a.v<b.v;
}
}posi[N],nega[N];
int pc,nc;
void dfs(int x,int pre,int cnt,LL S,int ID){
if (S>=0)
posi[++pc]=Node{cnt,S,ID};
else
nega[++nc]=Node{cnt,S,ID};
for (auto E : e[x])
if (E.fi!=pre&&!vis[E.fi])
dfs(E.fi,x,cnt+1,S+E.se,ID);
}
pair <int,LL> _1,_2;
void ckMax(pair <int,LL> _3){
if (_3.se>_1.se){
if (_3.fi!=_1.fi)
_2=_1;
_1=_3;
}
else if (_3.se>_2.se&&_3.fi!=_1.fi)
_2=_3;
}
void ckMin(pair <int,LL> _3){
if (_3.se<_1.se){
if (_3.fi!=_1.fi)
_2=_1;
_1=_3;
}
else if (_3.se<_2.se&&_3.fi!=_1.fi)
_2=_3;
}
int check(LL x){
_1=_2=mp(0,INF);
for (int i=1,j=nc;i<=pc;i++){
while (j>0&&posi[i].v+nega[j].v>=0)
ckMin(mp(nega[j].id,nega[j].v-x*nega[j].t)),j--;
if (posi[i].v-x*posi[i].t+(posi[i].id==_1.fi?_2.se:_1.se)<0)
return 1;
ckMin(mp(posi[i].id,posi[i].v-x*posi[i].t));
}
_1=_2=mp(0,-INF);
for (int i=pc,j=1;i>=1;i--){
while (j<=nc&&posi[i].v+nega[j].v<0)
ckMax(mp(nega[j].id,nega[j].v+x*nega[j].t)),j++;
if (posi[i].v+x*posi[i].t+(posi[i].id==_1.fi?_2.se:_1.se)>0)
return 1;
ckMin(mp(posi[i].id,posi[i].v+x*posi[i].t));
}
return 0;
}
void solve(int x){
Maxsize[0]=n+1;
root=pc=nc=0;
get_root(x,0);
vis[x=root]=1;
posi[++pc]=Node{0,0,x};
for (auto E : e[x])
if (!vis[E.fi])
dfs(E.fi,x,1,E.se,E.fi);
sort(posi+1,posi+pc+1);
sort(nega+1,nega+nc+1);
LL L=1,R=ans-1,mid;
while (L<=R){
mid=(L+R)>>1;
if (check(mid))
R=mid-1;
else
L=mid+1;
}
ans=min(ans,L);
for (auto E : e[x])
if (!vis[E.fi])
Size=size[E.fi],solve(E.fi);
}
int main(){
Size=n=read(),K=read();
for (int i=1;i<n;i++){
int a=read(),b=read();
LL c=read()-K;
ans=min(ans,abs(c)+1);
e[a].push_back(mp(b,c));
e[b].push_back(mp(a,c));
}
solve(1);
printf("%lld\n",ans-1);
return 0;
}
UOJ#276. 【清华集训2016】汽水 二分答案 点分治的更多相关文章
- [UOJ#276][清华集训2016]汽水[分数规划+点分治]
题意 给定一棵 \(n\) 个点的树,给定 \(k\) ,求 \(|\frac{\sum w(路径长度)}{t(路径边数)}-k|\)的最小值. \(n\leq 5\times 10^5,k\leq ...
- [UOJ#274][清华集训2016]温暖会指引我们前行
[UOJ#274][清华集训2016]温暖会指引我们前行 试题描述 寒冬又一次肆虐了北国大地 无情的北风穿透了人们御寒的衣物 可怜虫们在冬夜中发出无助的哀嚎 “冻死宝宝了!” 这时 远处的天边出现了一 ...
- UOJ276 [清华集训2016] 汽水 【二分答案】【点分治】【树状数组】
题目分析: 这种乱七八糟的题目一看就是点分治,答案有单调性,所以还可以二分答案. 我们每次二分的时候考虑答案会不会大于等于某个值,注意到系数$k$是无意义的,因为我们可以通过转化使得$k=0$. 合并 ...
- BZOJ.4738.[清华集训2016]汽水(点分治 分数规划)
BZOJ UOJ 记\(val_i\)是每条边的边权,\(s\)是边权和,\(t\)是经过边数,\(k\)是给定的\(k\). 在点分治的时候二分答案\(x\),设\(|\frac st-k|=x\) ...
- 并不对劲的uoj276. [清华集训2016]汽水
想要很对劲的讲解,请点击这里 题目大意 有一棵\(n\)(\(n\leq 50000\))个节点的树,有边权 求一条路径使该路径的边权平均值最接近给出的一个数\(k\) 输出边权平均值下取整的整数部分 ...
- BZOJ 4732 UOJ #268 [清华集训2016]数据交互 (树链剖分、线段树)
题目链接 (BZOJ) https://www.lydsy.com/JudgeOnline/problem.php?id=4732 (UOJ) http://uoj.ac/problem/268 题解 ...
- [UOJ#276]【清华集训2016】汽水
[UOJ#276][清华集训2016]汽水 试题描述 牛牛来到了一个盛产汽水的国度旅行. 这个国度的地图上有 \(n\) 个城市,这些城市之间用 \(n−1\) 条道路连接,任意两个城市之间,都存在一 ...
- UOJ 275. 【清华集训2016】组合数问题
UOJ 275. [清华集训2016]组合数问题 组合数 $C_n^m $表示的是从 \(n\) 个物品中选出 \(m\) 个物品的方案数.举个例子,从$ (1,2,3)(1,2,3)$ 三个物品中选 ...
- UOJ #269. 【清华集训2016】如何优雅地求和
UOJ #269. [清华集训2016]如何优雅地求和 题目链接 给定一个\(m\)次多项式\(f(x)\)的\(m+1\)个点值:\(f(0)\)到\(f(m)\). 然后求: \[ Q(f,n,x ...
随机推荐
- BZOJ4977[Lydsy1708月赛]跳伞求生——贪心+堆+模拟费用流
题目链接: 跳伞求生 可以将题目转化成数轴上有$n$个人和$m$个房子,坐标分别为$a_{i}$和$b_{i}$,每个人可以进一个他左边的房子,每个房子只能进一个人.每个房子有一个收益$c_{i}$, ...
- 【AGC015E】Mr.Aoki Incubator DP
题目描述 数轴上有\(n\)个人,每个人的位置是\(x_i\),速度是\(v_i\). 最开始有一些人感染了传染病. 如果某一时刻一个正常人和一个被感染的人处于同一位置,那么这个正常人也会被感染. 问 ...
- Python学习day9 函数Ⅰ(基础)
函数Ⅰ(基础) 三目运算 基本结构 v = 前面 if 条件 else 后面 #条件为真v=前面,条件为假v=后面.#等同于if 条件: v = '前面'else: v = '后面' ...
- tarjan模板
tarjan #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring& ...
- NOI-OJ 1.12 ID:10 素数对
整体思路 本题涉及大量素数的使用,故使用埃拉拖色尼算法提前计算出素数表可以避免大量.重复的计算. 判断素数对很简单,使用两个变量p1和p2代表素数表中的第一个和第二个素数,依次在表中向后移动,判断p2 ...
- UDP 单播、广播、多播
一.UDP广播 广播UDP与单播UDP的区别就是IP地址不同,广播使用广播地址255.255.255.255,将消息发送到在同一广播网络上的每个主机.值得强调的是:本地广播信息是不会被路由器转发.当然 ...
- 写博客常用MarkDown语法
目录 前言 1. 制作目录 2. 命令格式: 3. 超链接 4.上标和下标 5.引用 6.分割线 7.给图片添加图注 参考 前言 自己记性不是很好,导致每次写MarkDown文本时总是忘了一些重要 ...
- AWK如何打印从某一列到最后一列的内容
awk -F " " '{for (i=4;i<=NF;i++)printf("%s ", $i);print ""}' file
- 【原创】Linux基础之查看linux发行版以及内核版本
redhat查看发行版 # cat /etc/redhat-release CentOS Linux release 7.2.1511 (Core) 查看内核版本 # uname -aLinux $h ...
- 【原创】Linux基础之sudo
sudo允许用户以其他用户的身份(比如root)执行命令,比如切换用户.执行命令.读写文件等: 配置 sudo配置在:/etc/sudoers ## Sudoers allows particular ...