【BZOJ4738/UOJ#276】汽水(点分治,分数规划)
【BZOJ4738/UOJ#276】汽水(点分治,分数规划)
题面
题解
今天考试的题目,虽然说是写完了,但是感觉还是半懂不懂的来着。
代码基本照着\(Anson\)爷的码的,orz。(然后Anson爷的UOJrk1不保了)
首先拿到这道题目的一个比较显然的思路就是分数规划二分答案之后再点分治考虑是否有满足二分条件的链。
考虑条件是什么呢?(接下来写的时候为了方便,把所有的边权默认全部减去了一个\(K\),这样子就是要求平均值的绝对值最小的链了)
因为要的是绝对值最小,那么我们二分了这个绝对值\(mid\)之后,只有两种情况,要么平均值小于\(0\),并且大于\(-mid\),或者大于\(0\)并且小于\(mid\)。移项之后变成了权值和减去边的数量乘以二分值的结果与\(0\)的大小关系。这两种情况分开考虑计算。
那么,我们要做的就是确定分治重心之后,求出过重心的所有链。先考虑其子树中的每一个点,记录三元组,分别表示权值和,边的数量,以及从哪个子树来的(显然只有两个不同子树中的链才能拼在一起),按照权值和排序之后考虑如何拼接。以权值和大于\(0\)为例。
对于每个权值和大于\(0\)的链从小往大加入贡献,找到权值最小的链满足与当前链的权值和大于\(0\),因为这个最小值是一段区间,所以维护下来两个最大值与当前权值为正的链拼接\(check\)是否满足条件即可。
好难说清楚啊,看下代码就懂了。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define MAX 55000
inline ll read()
{
ll x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
struct Line{int v,next;ll w;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v,ll w){e[cnt]=(Line){v,h[u],w};h[u]=cnt++;}
int sz[MAX],rt,Sz,mx;bool vis[MAX];
struct Node{ll v,s,t;}S[MAX];int top;
bool operator<(Node a,Node b){return a.s<b.s;}
ll ans=1ll<<60,K;int n;
void getroot(int x,int ff)
{
sz[x]=1;int ret=0;
for(int i=h[x];i;i=e[i].next)
{
int v=e[i].v;if(v==ff||vis[v])continue;
getroot(v,x);sz[x]+=sz[v];
ret=max(ret,sz[v]);
}
ret=max(ret,Sz-sz[x]);
if(ret<mx)mx=ret,rt=x;
}
void dfs(int u,int fa,int dep,ll sum,int tp)
{
S[++top]=(Node){dep,sum,tp};
for(int i=h[u];i;i=e[i].next)
if(e[i].v!=fa&&!vis[e[i].v])
dfs(e[i].v,u,dep+1,sum+e[i].w,tp);
}
int pos;
pair<ll,ll> A,B;
void upd(pair<ll,ll> c)
{
if(c.first<B.first)
{
if(c.first<A.first)
{
if(c.second!=A.second)B=A;
A=c;
}
else if(c.second!=A.second)B=c;
}
}
bool check1(ll k)
{
A=B=make_pair(1ll<<60,0);
for(int i=pos,j=pos-1;i<=top;++i)
{
while(j&&S[i].s+S[j].s>=0)
upd(make_pair(S[j].s-k*S[j].v,S[j].t)),--j;
if((A.second==S[i].t?B.first:A.first)<k*S[i].v-S[i].s)return true;
upd(make_pair(S[i].s-k*S[i].v,S[i].t));
}
return false;
}
bool check2(ll k)
{
A=B=make_pair(1ll<<60,0);
for(int i=pos-1,j=pos;i;--i)
{
while(j<=top&&S[i].s+S[j].s<0)upd(make_pair(-S[j].s+k*S[j].v,S[j].t)),++j;
if((A.second==S[i].t?B.first:A.first)<-k*S[i].v+S[i].s)return true;
upd(make_pair(-S[i].s+k*S[i].v,S[i].t));
}
return false;
}
void Divide(int x)
{
vis[x]=true;S[top=1]=(Node){0,0,0};
for(int i=h[x];i;i=e[i].next)
if(!vis[e[i].v])
dfs(e[i].v,x,1,e[i].w,e[i].v);
sort(&S[1],&S[top+1]);
for(pos=1;pos<=top&&S[pos].s<0;++pos);
ll l=1,r=ans-1;
while(l<=r)
{
ll mid=(l+r)>>1;
if(check1(mid)||check2(-mid))r=mid-1;
else l=mid+1;
}
ans=min(ans,l);
for(int i=h[x];i;i=e[i].next)
if(!vis[e[i].v])
Sz=mx=sz[e[i].v],getroot(e[i].v,x),Divide(rt);
}
int main()
{
n=read();K=read();
for(int i=1;i<n;++i)
{
int u=read(),v=read();ll w=read()-K;
Add(u,v,w);Add(v,u,w);ans=min(ans,abs(w)+1);
}
Sz=mx=n;getroot(1,0);
Divide(1);
printf("%lld\n",ans-1);
return 0;
}
【BZOJ4738/UOJ#276】汽水(点分治,分数规划)的更多相关文章
- BZOJ.4738.[清华集训2016]汽水(点分治 分数规划)
BZOJ UOJ 记\(val_i\)是每条边的边权,\(s\)是边权和,\(t\)是经过边数,\(k\)是给定的\(k\). 在点分治的时候二分答案\(x\),设\(|\frac st-k|=x\) ...
- [UOJ#276][清华集训2016]汽水[分数规划+点分治]
题意 给定一棵 \(n\) 个点的树,给定 \(k\) ,求 \(|\frac{\sum w(路径长度)}{t(路径边数)}-k|\)的最小值. \(n\leq 5\times 10^5,k\leq ...
- uoj#276. 【清华集训2016】汽水(分数规划+点分治)
传送门 没想到点分治那一层-- 首先不难发现这是个分数规划,先把所有的边长减去\(k\),二分答案,设为\(mid\),就是要求路径平均值\(ans\in[-mid,mid]\) 先来考虑\(ans\ ...
- 【UOJ276】【清华集训2016】汽水(分数规划+点分治)
点此看题面 大致题意: 给你一棵树,要求你选择一条树上路径,使得这条路径上边权的平均值与定值\(k\)的差的绝对值最小.求出这个最小值. 分数规划 看到平均值,首先就应该想到分数规划吧. 我们二分答案 ...
- [UOJ#276]【清华集训2016】汽水
[UOJ#276][清华集训2016]汽水 试题描述 牛牛来到了一个盛产汽水的国度旅行. 这个国度的地图上有 \(n\) 个城市,这些城市之间用 \(n−1\) 条道路连接,任意两个城市之间,都存在一 ...
- 【BZOJ 1758】【WC 2010】重建计划 分数规划+点分治+单调队列
一开始看到$\frac{\sum_{}}{\sum_{}}$就想到了01分数规划但最终还是看了题解 二分完后的点分治,只需要维护一个由之前处理过的子树得出的$tb数组$,然后根据遍历每个当前的子树上的 ...
- [WC2010]重建计划(分数规划+点分治+单调队列)
题目大意:给定一棵树,求一条长度在L到R的一条路径,使得边权的平均值最大. 题解 树上路径最优化问题,不难想到点分治. 如果没有长度限制,我们可以套上01分数规划的模型,让所有边权减去mid,求一条路 ...
- P2877 [USACO07JAN]牛校Cow School(01分数规划+决策单调性分治)
P2877 [USACO07JAN]牛校Cow School 01分数规划是啥(转) 决策单调性分治,可以解决(不限于)一些你知道要用斜率优化却不会写的问题 怎么证明?可以暴力打表 我们用$ask(l ...
- [WC2010][BZOJ1758]重建计划-[二分+分数规划+点分治]
Description 传送门 Solution 看到那个式子,显然想到分数规划...(不然好难呢) 然后二分答案,则每条边的权值设为g(e)-ans.最后要让路径长度在[L,U]范围内的路径权值&g ...
随机推荐
- Trusted Block Chain Summit(2018.10.09)
时间:2018.10.09地点:北京金隅喜来登大酒店
- BootStrap学习(1)
一.Bootstrap简介 BootStrap是由Twitter推出的前端框架,2011 年八月在 GitHub 上发布,BootStrap是基于Html,Css,Javascript的,可用于快速开 ...
- React 开发注意事项
引用自定义组件的时候,组件名称首字母大写 import CustomComponent from "./customComponent "; render(){ return ( ...
- 《RabbitMQ Tutorial》译文 第 1 章 简介
原文来自 RabbitMQ 英文官网的教程(1.Introduction),其示例代码采用了 .NET C# 语言. RabbitMQ is a message broker: it accepts ...
- A. A Prank
题意 有数列从小到大排列,都是不同范围1~ 1000,问你最多去掉多少个数字还能复原 由于wrong很多发所以写一下 链接 [http://codeforces.com/contest/1062/pr ...
- Individual Project复审
复审代码的来源:12061162 王骜 王骜同学的代码注释较多,读起来还是比较容易懂. 代码遵从模块化思想,各个模块之间分工明确,功能重复少,模块之间联系紧密,相互调用明确. 处理单词过程运用了正则表 ...
- Linux内核设计与实现 第十七章
1. 设备类型 linux中主要由3种类型的设备,分别是: 设备类型 代表设备 特点 访问方式 块设备 硬盘,光盘 随机访问设备中的内容 一般都是把设备挂载为文件系统后再访问 字符设备 键盘,打印机 ...
- MSF MS11-050/10-087/Adobe攻击实践及内存保护技术
MSF MS11-050/10-087/Adobe攻击实践及内存保护技术 内存攻击指的是攻击者利用软件安全漏洞,构造恶意输入导致软件在处理输入数据时出现非预期错误,将输入数据写入内存中的某些特定敏感位 ...
- Mooc总结——Linux内核分析
朱荟潼+ 原创作品转载请注明出处 :<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 学习笔记链接汇总 第 ...
- Centos wget命令 not found解决方法
如果已经有了yun源,则可通过yun源命令来安装wget. 如下所示: 2.yum安装yum -y install wget 即可安装: