【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 ...
随机推荐
- ASP.NET Core 2.1 源码学习之 Options[2]:IOptions
在 上一章 中,介绍了Options的注册,而在使用时只需要注入 IOption<T> 即可: public ValuesController(IOptions<MyOptions& ...
- [JSOI2016]无界单词[动态规划、kmp]
题意 题目链接 分析 对于第一问,枚举最终串最小的相同前后缀来统计答案. 由于最小的相同前后缀也是无界单词,所以可以考虑先求解子问题. 定义状态 \(f(i)\) 表示长度为 \(i\) 的串中有多少 ...
- ExtJS框架基础:事件模型及其常用功能
前言 工作中用ExtJS有一段时间了,Ext丰富的UI组件大大的提高了开发B/S应用的效率.虽然近期工作中天天都用到ExtJS,但很少对ExtJS框架原理性的东西进行过深入学习,这两天花了些时间学习了 ...
- Html5前端笔记
获取Dpi 在 window.load中添加: (function(){ if (!window.screen.deviceXDPI){ var tmpNode = document.createEl ...
- C_数据结构_递归不同函数间调用
# include <stdio.h> void f(); void g(); void k(); void f() { printf("FFFF\n"); g(); ...
- cf946d 怎样逃最多的课dp
来源:codeforces D. Timetable Ivan is a student at Berland ...
- Linux内核分析作业 NO.8 完结撒花~~~
进程的切换和系统的一般执行过程 于佳心 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-10000 ...
- Struts2中的图片验证码
1.Struts中建一个action <action name="Code" class="LoginAction" method="code& ...
- PAT 1007 素数对猜想
https://pintia.cn/problem-sets/994805260223102976/problems/994805317546655744 让我们定义 d~n~ 为:d~n~ = p~ ...
- FreeMarker example all in one
Pick up from http://demojava.iteye.com/blog/800204