点此看题面

大致题意: 给你一棵树,要求你选择一条树上路径,使得这条路径上边权的平均值与定值\(k\)的差的绝对值最小。求出这个最小值。

分数规划

看到平均值,首先就应该想到分数规划吧。

我们二分答案\(x\),设选择了\(m\)条边,每条边边权为\(a_i\)。

则答案\(x\)合法,需要满足:

\[|\frac{\sum_{i=1}^ma_i}m-k|\le x
\]

拆绝对值:

\[-x\le\frac{\sum_{i=1}^ma_i}m-k\le x
\]

每个式子同时乘上\(m\),去分母:

\[-mx\le \sum_{i=1}^ma_i-mk\le mx
\]

可以将这整个式子分成两部分考虑。

对于左半部分:

\[\sum_{i=1}^ma_i-mk\ge -mx
\]

\[\sum_{i=1}^ma_i-mk+mx\ge0
\]

\[\sum_{i=1}^m(a_i-k+x)\ge0
\]

对于右半部分:

\[\sum_{i=1}^ma_i-mk\le mx
\]

\[\sum_{i=1}^ma_i-mk-mx\le0
\]

\[\sum_{i=1}^m(a_i-k-x)\le0
\]

也就是说,我们只需要判断是否存在一条路径,同时满足以下两个条件:

\[\sum_{i=1}^m(a_i-k+x)\ge0
\]

\[\sum_{i=1}^m(a_i-k-x)\le0
\]

不难发现,如果设\(v_i=a_i-k\),那么式子就变成了:

\[\sum_{i=1}^m(v_i+x)\ge0
\]

\[\sum_{i=1}^m(v_i-x)\le0
\]

其实,这也就是在读入边权时就将其减去\(k\),便能将式子化简许多。

点分治

对于树上路径问题,最容易想到的就是点分治了。

对于每一个分治中心\(rt\),我们先遍历其子树,对于其子树内每一个点\(i\),存储下三个信息:

  1. \(i\)在\(rt\)哪一个子节点的子树中。
  2. \(i\)到\(rt\)的路径上的\(\sum(v+x)\)。
  3. \(i\)到\(rt\)的路径上的\(\sum(v-x)\)。

如果要符合条件,就需要存在一条路径或者两条不在同一子节点的子树中的路径的\(\sum(v+x)\ge0\)且\(\sum(v-x)\le0\)。

对此,我们只需要将节点信息按\(\sum(v+x)\)从小到大排序,枚举一条路径,用双指针思想来维护另一路径可选区间。

记录下可选区间中\(\sum(v-x)\)的最小值和与最小值不在同一子节点的子树内的另一最小值,然后就可以判断出对于这条路径是否存在另一条路径与其匹配能够符合条件了。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 50000
#define LL long long
#define DB long double
#define INF 1e18
#define eps 1e-4
#define add(x,y,z) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].v=z)
#define Gmax(x,y) (x<(y)&&(x=(y)))
using namespace std;
int n,ee,lnk[N+5];struct edge {int to,nxt;LL v;}e[N<<1];
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define pc(c) (C==E&&(clear(),0),*C++=c)
#define tn (x<<3)+(x<<1)
#define D isdigit(c=tc())
char c,*A,*B,FI[FS];
public:
I FastIO() {A=B=FI;}
Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
#undef D
}F;
class DotDivideSolver//点分治
{
private:
DB p;int rt,sum,cnt,vis[N+5],Sz[N+5],Mx[N+5];
struct data//存储一个节点的信息
{
int i;DB x,y;I data(CI p=0,Con DB& a=0,Con DB& b=0):i(p),x(a),y(b){}
I bool operator < (Con data& o) Con {return x<o.x;}//用于排序
}s[N+5],Mn,Sn;
I void GetRt(CI x,CI lst=0)//找重心
{
Sz[x]=1,Mx[x]=0;for(RI i=lnk[x];i;i=e[i].nxt) !vis[e[i].to]&&
e[i].to^lst&&(GetRt(e[i].to,x),Sz[x]+=Sz[e[i].to],Gmax(Mx[x],Sz[e[i].to]));
Gmax(Mx[x],sum-Sz[x]),Mx[x]<Mx[rt]&&(rt=x);
}
I void Travel(CI x,CI lst,CI ty,Con DB& a,Con DB& b)//遍历某一子节点的子树
{
s[++cnt]=data(ty,a,b);for(RI i=lnk[x];i;i=e[i].nxt)//存储下节点信息
!vis[e[i].to]&&e[i].to^lst&&(Travel(e[i].to,x,ty,a+e[i].v+p,b+e[i].v-p),0);
}
I bool Work(CI x,CI lst=0)
{
RI i,j;for(vis[x]=1,cnt=0,i=lnk[x];i;i=e[i].nxt)//遍历子树
!vis[e[i].to]&&e[i].to^lst&&(Travel(e[i].to,x,e[i].to,e[i].v+p,e[i].v-p),0);
for(Mn=Sn=data(0,INF,INF),sort(s+1,s+cnt+1),i=1,j=cnt;i<=cnt;++i)//排序
{
if(s[i].x>=0&&s[i].y<=0) return true;//如果这单条路径符合条件,返回true
W(j&&s[i].x+s[j].x>=0)//双指针
s[j].y<Mn.y?(Mn.i^s[j].i&&(Sn=Mn,0),Mn=s[j],0)//与最小值相比较
:Mn.i^s[j].i&&s[j].y<Sn.y&&(Sn=s[j],0),--j;//与另一最小值相比较
if(s[i].i^Mn.i?(s[i].y+Mn.y<0):(s[i].y+Sn.y<0)) return true;//判断是否合法
}
for(i=lnk[x];i;i=e[i].nxt) if(!vis[e[i].to])//递归处理子树
if(rt=0,sum=Sz[e[i].to],GetRt(e[i].to,x),Work(rt,x)) return true;
return false;
}
public:
I void Clear() {memset(vis,0,sizeof(vis));}//清空
I bool Solve(Con DB& x) {return p=x,Clear(),Mx[rt=0]=sum=n,GetRt(1),Work(rt);}//点分验证二分的答案
}D;
int main()
{
RI i,x,y;LL z,t;for(F.read(n),F.read(t),i=1;i^n;++i)
F.read(x),F.read(y),F.read(z),add(x,y,z-t),add(y,x,z-t);//建边,将边权减k
DB l=0,r=INF,mid;W(r-l>eps) D.Solve(mid=(l+r)/2)?r=mid:l=mid;//二分答案
return printf("%.0lf",(double)floor(r)),0;//输出答案,注意精度
}

【UOJ276】【清华集训2016】汽水(分数规划+点分治)的更多相关文章

  1. [UOJ#276][清华集训2016]汽水[分数规划+点分治]

    题意 给定一棵 \(n\) 个点的树,给定 \(k\) ,求 \(|\frac{\sum w(路径长度)}{t(路径边数)}-k|\)的最小值. \(n\leq 5\times 10^5,k\leq ...

  2. UOJ276 [清华集训2016] 汽水 【二分答案】【点分治】【树状数组】

    题目分析: 这种乱七八糟的题目一看就是点分治,答案有单调性,所以还可以二分答案. 我们每次二分的时候考虑答案会不会大于等于某个值,注意到系数$k$是无意义的,因为我们可以通过转化使得$k=0$. 合并 ...

  3. 并不对劲的uoj276. [清华集训2016]汽水

    想要很对劲的讲解,请点击这里 题目大意 有一棵\(n\)(\(n\leq 50000\))个节点的树,有边权 求一条路径使该路径的边权平均值最接近给出的一个数\(k\) 输出边权平均值下取整的整数部分 ...

  4. BZOJ.4738.[清华集训2016]汽水(点分治 分数规划)

    BZOJ UOJ 记\(val_i\)是每条边的边权,\(s\)是边权和,\(t\)是经过边数,\(k\)是给定的\(k\). 在点分治的时候二分答案\(x\),设\(|\frac st-k|=x\) ...

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

    [UOJ#276][清华集训2016]汽水 试题描述 牛牛来到了一个盛产汽水的国度旅行. 这个国度的地图上有 \(n\) 个城市,这些城市之间用 \(n−1\) 条道路连接,任意两个城市之间,都存在一 ...

  6. [清华集训2016]石家庄的工人阶级队伍比较坚强——三进制FWT

    题目链接: [清华集训2016]石家庄的工人阶级队伍比较坚强 题目大意:有$n=3^m$个人玩石头剪刀布,共$t$轮游戏,每轮每个人要和包括自己的所有人各进行$m$次石头剪刀布.每个人在$m$轮中的决 ...

  7. UOJ #274. 【清华集训2016】温暖会指引我们前行 [lct]

    #274. [清华集训2016]温暖会指引我们前行 题意比较巧妙 裸lct维护最大生成树 #include <iostream> #include <cstdio> #incl ...

  8. UOJ_274_[清华集训2016]温暖会指引我们前行_LCT

    UOJ_274_[清华集训2016]温暖会指引我们前行_LCT 任务描述:http://uoj.ac/problem/274 本题中的字典序不同在于空串的字典序最大. 并且题中要求排序后字典序最大. ...

  9. UOJ 275. 【清华集训2016】组合数问题

    UOJ 275. [清华集训2016]组合数问题 组合数 $C_n^m $表示的是从 \(n\) 个物品中选出 \(m\) 个物品的方案数.举个例子,从$ (1,2,3)(1,2,3)$ 三个物品中选 ...

  10. UOJ #269. 【清华集训2016】如何优雅地求和

    UOJ #269. [清华集训2016]如何优雅地求和 题目链接 给定一个\(m\)次多项式\(f(x)\)的\(m+1\)个点值:\(f(0)\)到\(f(m)\). 然后求: \[ Q(f,n,x ...

随机推荐

  1. 批发市场收记账管理系统(iPad与手机版)水产批发市场客户欠账、还款管理水产宝介绍 第八章 财务(应收账款,应付账款,已收账款,已付账款)

    1.财务 ① 财务模块主功能(收支记账,记账类别,应收账款,应付账款,支付方式管理,账期管理) ② 支付设置 a 系统内置支付方式有6种 b 新增支付方式 新增支付方式     主要上传支付方式图标. ...

  2. Linux中jar包启动和jar包后台运行

    Linux 运行jar包命令如下: 方式一: java -jar shareniu.jar 特点:当前ssh窗口被锁定,可按CTRL + C打断程序运行,或直接关闭窗口,程序退出 那如何让窗口不锁定? ...

  3. diango创建一个app

    创建一个app terminal里执行命令 python manage.py startapp app名称 注册 settings配置 INSTALLED_APPS = [ 'app01', 'app ...

  4. [Go] 使用net包作为tcp客户端读取http

    1.tcp的客户端,并且直接读取http协议的全部内容,每次读取4096字节,直到最后一个字节是\n并且读取的长度小于4096 conn, err := net.Dial("tcp" ...

  5. centos7 链路聚合+KVM桥接连网

    一.两个物理网卡做链路聚合(em3,em4) 1)创建team类型的网卡,连接别名为team0,使用的模式为activebackup-主备/loadbalance-负载均衡nmcli con add ...

  6. java之==操作符和equals操作符

    ==操作符: 基本数据类型比较值: 引用数据类型比较引用(是否指向同一个对象) equals操作符: 引用数据类型比较引用(是否指向同一个对象) 对于String.File.Date.包装类来说,只比 ...

  7. 如何完成述职报告或年终总结PPT

    对于我们 打工仔 职场人士来说,年有年度总结,月有月度报告,指不定有些小伙伴还会有周报和日报,不仅枯燥,而且浪费时间,头都要炸了 ,简直太有趣了呢. 所以,如何准确快速的写完述职报告呢? 这是个好问题 ...

  8. github用户注册和仓库创建

    访问github官网:https://github.com/,点击注册进入注册页面 输入用户名,电子邮箱和密码后点击下一步 邮箱验证,收到github的验证邮箱,打开后点击验证 选择个人计划 创建仓库 ...

  9. 【分析工具】阿里巴巴Arthas--线上问题分析利器

    目录 1. Arthas是什么 2. Arthas能解决什么问题 3. 快速安装 第一步:下载 第二步:运行 第三步:选择进程 4. 实战使用 5. 总结 本博客转载自阿里开源的 Java 诊断工具 ...

  10. Selenium(七):选择框(radio框、checkbox框、select框)

    1. 选择框 本章使用的html代码: <!DOCTYPE html> <html lang="en"> <head> <meta cha ...