想要很对劲的讲解,请点击这里

题目大意

有一棵\(n\)(\(n\leq 50000\))个节点的树,有边权

求一条路径使该路径的边权平均值最接近给出的一个数\(k\)

输出边权平均值下取整的整数部分

题解

有关路径的边权平均值可以二分来做

假设已二分出边权平均值与\(k\)的差不超过\(m\),则应存在一条简单路径,路径上的\(p\)条边满足:

\[-m\leq\frac{\sum_{i=1}^{p}{w_i}}{p}-k\leq m
\]

两边同乘\(p\),得:

\[-m*p\leq\sum_{i=1}^{p}{w_i-k}\leq m*p
\]

也就是$$\begin{cases}{\sum_{i=1}^{p}{(w_i-k+m)}}\geq 0\\sum_{i=1}^{p}{(w_i-k-m)}\leq 0\end{cases}$$

那么设\(a_i=w_i-k+m,b_i=w_i-k-m\),问题就变成了判断图中是否存在一条简单路径,满足\(\Sigma a_i\geq 0\)且\(\Sigma b_i\leq 0\)

这种在树上找满足某个条件的链的问题可以用点分治解决

设某点\(i\)到当前区域的重心的简单路径上,\(a_k\)之和为\(A_i\),\(b_k\)之和为\(B_i\)

则问题转化为找两点\(i\),\(j\)不属于当前区域的重心的同一子树,且\(A_i+A_j\geq0\),\(B_i+B_j\leq0\)

也可以看成是对于点\(i\),所有\(A_j\geq-A_i\)的点\(j\)中,\(B_j\)的最小值是否小于\(-B_i\)

这个可以用平衡树或权值线段树来维护

再算上二分、点分治的时间复杂度,总时间复杂度使\(\Theta(n\space log^3 n)\)的,看上去有点悬

考虑加一些小优化,比如:

1.预处理点分树,就不用二分时每次判断都求一遍重心;

2.当找到一条合法的链后,立刻退出;

3.没有必要把当前区域的重心的最后一个儿子的子树里的点的信息加入平衡树或权值线段树

...

然而还是跑得很慢…

#include<algorithm>
#include<cmath>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#define maxn 50010
#define maxm (maxn<<1)
#define rep(i,x,y) for(int i=(x);i<=(y);++i)
#define dwn(i,x,y) for(int i=(x);i>=(y);--i)
#define ls son[u][0]
#define rs son[u][1]
#define eps 5e-3
#define LD long double
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')ch=getchar(),f=-1;
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*f;
}
const double inf=(1.0/0.0);
double mx[maxn],key[maxn],key2[maxn],dep[maxn],dep2[maxn],w[maxm],K,L,R,mid,ans;
int n,fa[maxn],son[maxn][2],rt,vis[maxn],ww[maxn],siz[maxn],nowsiz,mnsiz,wt,nxt[maxm],fir[maxn],v[maxm],cnt,nd,yes;
int nt[maxn],vv[maxn],fs[maxn],cnt2,root;
void ade(int u1,int v1,double w1){v[cnt]=v1,nxt[cnt]=fir[u1],w[cnt]=w1,fir[u1]=cnt++;}
void ade2(int u1,int v1){vv[cnt2]=v1,nt[cnt2]=fs[u1],fs[u1]=cnt2++;}
void getsiz(int u,int fa)
{
siz[u]=1;int nowmx=0;
for(int k=fir[u];k!=-1;k=nxt[k])
if(v[k]!=fa&&!vis[v[k]])getsiz(v[k],u),siz[u]+=siz[v[k]],nowmx=max(nowmx,siz[v[k]]);
nowmx=max(nowmx,nowsiz-siz[u]);
if(nowmx<mnsiz)mnsiz=nowmx,wt=u;
}
void getwt(int u,int fa,int sumsiz)
{
nowsiz=sumsiz,mnsiz=n+1,getsiz(u,0),vis[wt]=1;
int now=wt;
if(sumsiz==n)root=now;
else ade2(fa,now);
for(int k=fir[now];k!=-1;k=nxt[k])
if(!vis[v[k]])getwt(v[k],now,siz[v[k]]>siz[now]?sumsiz-siz[now]:siz[v[k]]);
}
void pu(int u)
{
if(!u)return;
mx[u]=key2[u];
if(ls)mx[u]=min(mx[u],mx[ls]);
if(rs)mx[u]=min(mx[u],mx[rs]);
return;
}
int getso(int u){return son[fa[u]][0]!=u;}
void rot(int u)
{
register int fu=fa[u],ffu=fa[fu],l=getso(u),fl=getso(fu),r=l^1,rson=son[u][r];
if(ffu)son[ffu][fl]=u;son[u][r]=fu,son[fu][l]=rson;if(rson)fa[rson]=fu;fa[u]=ffu,fa[fu]=u;
pu(fu),pu(u);
}
void ins(double x,double y)
{
register int u=rt,lst=0,to;
while(u&&key[u]!=x)lst=u,to=x>key[u]?1:0,u=son[u][to];
if(!u)
{
u=++nd,ls=rs=0,key[u]=x,key2[u]=y,mx[u]=y,ww[u]=rand()*rand()%63427919,fa[u]=lst;
if(lst)son[lst][to]=u/*,upd2(lst)*/;
else rt=u;
while(fa[u]&&ww[fa[u]]>ww[u])rot(u);
if(!fa[u])rt=u;
}
else {key2[u]=min(key2[u],y),mx[u]=min(mx[u],y);while(u)pu(u),u=fa[u];}return;
}
double ask(double x)
{
int u=rt;double res=inf;
while(u)
{
if(x<=key[u]){res=min(res,min(key2[u],rs?mx[rs]:inf));u=ls;}
else u=rs;
}
return res;
}
void asky(int u,int fa)
{
double res=ask(-dep[u]);
if(res!=inf){if(res+dep2[u]<=0){yes=1;return;}}
for(int k=fir[u];k!=-1;k=nxt[k])
if(!vis[v[k]]&&v[k]!=fa)
{
dep[v[k]]=dep[u]+w[k]+mid,dep2[v[k]]=dep2[u]+w[k]-mid,asky(v[k],u);
if(yes)return;
}
}
void addy(int u,int fa)
{
ins(dep[u],dep2[u]);
for(int k=fir[u];k!=-1;k=nxt[k])
if(!vis[v[k]]&&v[k]!=fa)addy(v[k],u);
}
void reset(){rt=0;nd=0,mx[0]=inf;}
void gety(int now)
{
reset();
ins(0,0);
vis[now]=1;
int lst=0;
for(int k=fir[now];k!=-1;k=nxt[k])if(!vis[v[k]])lst=v[k];
for(int k=fir[now];k!=-1;k=nxt[k])
{
if(!vis[v[k]])
{
dep[v[k]]=w[k]+mid,dep2[v[k]]=w[k]-mid;
asky(v[k],now);
if(yes)return;
if(v[k]!=lst)addy(v[k],now);
}
}
for(int k=fs[now];k!=-1;k=nt[k])
{gety(vv[k]);if(yes)return;}
}
int main()
{
srand(time(0));
nowsiz=n=read();scanf("%lf",&K);
memset(fir,-1,sizeof(fir));
memset(fs,-1,sizeof(fs));
rep(i,1,n-1){int x=read(),y=read();double z;scanf("%lf",&z);R=max(R,z-K<0.0?K-z:z-K),ade(x,y,z-K),ade(y,x,z-K);}
getwt(1,0,n);
ans=R;
while(R-L>=eps)
{
mid=(R+L)/2.0;yes=0;
rep(i,1,n)vis[i]=0;
gety(root);
if(yes)ans=min(ans,mid),R=mid;
else L=mid;
}
printf("%.0lf",floor(ans));
return 0;
}

并不对劲的uoj276. [清华集训2016]汽水的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  9. 【UOJ274】【清华集训2016】温暖会指引我们前行 LCT

    [UOJ274][清华集训2016]温暖会指引我们前行 任务描述 虽然小R住的宿舍楼早已来了暖气,但是由于某些原因,宿舍楼中的某些窗户仍然开着(例如厕所的窗户),这就使得宿舍楼中有一些路上的温度还是很 ...

随机推荐

  1. POJ 1386 单词接龙问题

    题目大意: 给一堆字母,让它们进行接龙,要头对尾能够接的上,问有没有一种方法让所有成语都完成接龙 这道题实际上是在考虑是否存在一条欧拉通路,每个单词产生一条有向线段,由第一个字母指向最后一个字母 这道 ...

  2. 在RedHat 5下安装Oracle 10g详解(转)

    在RedHat 5下安装Oracle 10g详解(转) Posted on 2012-09-14 13:26 疯狂 阅读(5075) 评论(0)  编辑  收藏 所属分类: database .uni ...

  3. gridview读取Excel文件中的数据,并将其导入数据库

    原文发布时间为:2008-10-16 -- 来源于本人的百度文章 [由搬家工具导入] //将需要导入的文件上传到服务器           string filePath = "" ...

  4. Python基础教程笔记——第5章:条件,循环和其他语句

    5.1 print和import的更多信息 1. print()3.0之后print不再是语句,而是函数, >>> print('udg',12,13)   udg 12 13 &g ...

  5. python学习之 - 时间模块

    时间模块模块名:time时间模块的转换流程图. UTC:英国格林威治时间.时间戳作用:是用来进行时间计算的.进行加减时间.注意:时间计算是用秒为单位time.process_time():测量处理器运 ...

  6. HDU 5360 【优先队列+贪心】

    题意: 给定N个无序区间. 对合法区间的定义是: 在这个区间之前已经选出了至少l个合法区间,最多选出了r个合法区间.则该区间为合法区间. 输出最多能挑选出多少个合法区间,并输出合法区间的数量. 思路: ...

  7. the attribute buffer size is too small 解决方法

    在进行查询的时候引发The attribute buffer size is too small错误解决 http://bbs.esrichina-bj.cn/esri/viewthread.php? ...

  8. unity3d 摄像机抖动特效

    摄像机抖动特效 在须要的地方调用CameraShake.Shake()方法就能够  

  9. 汝佳大神的紫书上写错了?uva10048

    算法竞赛入门经典第二版的365页例题11-5噪音.应该是"之和"换成"取最大值","取最小值"还是取最小值 假设我错了,请大家务必指点小弟 ...

  10. easyUI 验证控件应用、自己定义、扩展验证 手机号码或电话话码格式

    easyUI 验证控件应用.自己定义.扩展验证 手机号码或电话话码格式 在API中   发现给的demo 中没有这个验证,所以就研究了下. 相关介绍省略,直接上代码吧! watermark/2/tex ...