[IOI2018]会议——分治+线段树
题目链接:
题目大意:有$n$座山峰,每座山峰有一个高度,有$q$次询问,每次需要确定一个开会山峰使$[l,r]$所有山峰上的人都前往开会山峰,一个山峰的人去开会的代价为从这个山峰到开会山峰区间内山峰高度的最大值,对于每次询问求最小代价和。
还是按照编号都从$1$开始讲。
可以发现对于一个询问如果确定了开会地址那么答案只和每个点到开会点区间最大值有关。
而题目又没有强制在线,我们可以按区间最大值来分治。
我们设对于区间$[l,r]$的答案是$ans(l,r)$,区间中的最大值位于$mid$处(即$h[mid]$是区间最大值)。
那么显然答案选定的开会点一定在mid的左侧或右侧,$ans(l,r)=min{ans(l,mid-1)+h[mid]*(r-mid+1),ans(mid+1,r)+h[mid]*(mid-l+1)}$。
即如果开会点在$mid$左边那么$mid$及右边所有点到开会点的代价都是$h[mid]$,如果开会点在$mid$右边那么$mid$及左边所有点到开会点的代价都是$h[mid]$。
因此,$ans(l,r)$要由$ans(l,mid-1)$与$ans(mid+1,r)$合并而来。
那么我们可以确定一个大致思路:找出当前区间最大值位置然后分治左右区间,在得出左右区间的答案之后再处理询问区间最大值是当前区间最大值的询问,然后再递归回上一些层。
区间最大值很好求,用ST表维护一下即可,那么对于每个区间的ans怎么维护?
对于左端点为l的区间难道要同时维护出$ans(l,l),ans(l,l+1),ans(l,l+2)$……吗?
显然并不需要,我们发现对于每一次递归的区间$[l,r]$,需要用到左端点为$l$的$ans$只有$ans(l,mid-1)$和$ans(l,r)$。
其中前者对应处理当前层询问前递归合并上来的答案,后者则对应处理完当前层询问后需要递归合并上去的答案。
也就是说对于一个点$x$,在同一时刻只需要维护它作为一个区间左端点和右端点时这两个答案。
那么我们就可以用线段树来实现,对于线段树上每个叶子结点维护$lm$和$rm$两个信息,分别表示这个点是区间左/右端点时的区间答案。
因为无法确定每个点在回溯到上一层后会向左扩展还是向右扩展即不确定每个点位于上一层最高点的右边还是左边,所以我们开两棵线段树分别维护这两种情况。其中每棵线段树只维护$lm$和$rm$中的一种。
总结一下大体思路:对于当前区间找到区间最大值位置并递归左右子区间,回溯时处理当前区间需要处理的询问,对于每个$ans(x,mid-1)(l<=x<=mid-1)$更新为$ans(x,r)$;对于每个$ans(mid+1,x)(mid+1<=x<=r)$更新为$ans(l,x)$。
因为每个点被当做区间最大值一次且每个询问被处理一次,所以时间复杂度是O((n+q)logn)。
如果还是不太明白可以看代码的具体实现。
#include"meetings.h"
#include<set>
#include<map>
#include<queue>
#include<cmath>
#include<stack>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int n,q;
int h[750010];
int ql[750010];
int qr[750010];
int f[750010][21];
int g[750010][21];
int ln[750010];
vector<int>pos[750010];
ll res[750010];
struct miku
{
ll lm[4000010];
ll rm[4000010];
ll k[4000010];
ll b[4000010];
int a[4000010];
void cover(int rt)
{
a[rt]=1;
lm[rt]=rm[rt]=k[rt]=b[rt]=0;
}
void add(int rt,int l,int r,ll K,ll B)
{
k[rt]+=K;
b[rt]+=B;
lm[rt]+=K*l+B;
rm[rt]+=K*r+B;
}
void pushup(int rt)
{
lm[rt]=lm[rt<<1];
rm[rt]=rm[rt<<1|1];
}
void pushdown(int rt,int l,int r)
{
int mid=(l+r)>>1;
if(a[rt])
{
a[rt]=0;
cover(rt<<1);
cover(rt<<1|1);
}
if(k[rt]||b[rt])
{
add(rt<<1,l,mid,k[rt],b[rt]);
add(rt<<1|1,mid+1,r,k[rt],b[rt]);
k[rt]=b[rt]=0;
}
}
void change(int rt,int l,int r,int L,int R,ll val)
{
if(L<=l&&r<=R)
{
add(rt,l,r,0,val);
return ;
}
int mid=(l+r)>>1;
pushdown(rt,l,r);
if(L<=mid)
{
change(rt<<1,l,mid,L,R,val);
}
if(R>mid)
{
change(rt<<1|1,mid+1,r,L,R,val);
}
pushup(rt);
}
void merge(int rt,int l,int r,int L,int R,ll K,ll B)
{
if(L<=l&&r<=R)
{
ll LV=K*l+B;
ll RV=K*r+B;
if(LV>=lm[rt]&&RV>=rm[rt])
{
return ;
}
if(LV<=lm[rt]&&RV<=rm[rt])
{
cover(rt);
add(rt,l,r,K,B);
return ;
}
}
pushdown(rt,l,r);
int mid=(l+r)>>1;
if(L<=mid)
{
merge(rt<<1,l,mid,L,R,K,B);
}
if(R>mid)
{
merge(rt<<1|1,mid+1,r,L,R,K,B);
}
pushup(rt);
}
ll query_left(int rt,int l,int r,int x)
{
if(l==r)
{
return lm[rt];
}
pushdown(rt,l,r);
int mid=(l+r)>>1;
if(x<=mid)
{
return query_left(rt<<1,l,mid,x);
}
else
{
return query_left(rt<<1|1,mid+1,r,x);
}
}
ll query_right(int rt,int l,int r,int x)
{
if(l==r)
{
return rm[rt];
}
pushdown(rt,l,r);
int mid=(l+r)>>1;
if(x<=mid)
{
return query_right(rt<<1,l,mid,x);
}
else
{
return query_right(rt<<1|1,mid+1,r,x);
}
}
}s,t;
int cmp(int l,int r)
{
int len=ln[r-l+1];
if(f[l][len]>=f[r-(1<<len)+1][len])
{
return g[l][len];
}
else
{
return g[r-(1<<len)+1][len];
}
}
void solve(int l,int r)
{
if(l>r)
{
return ;
}
int mid=cmp(l,r);
solve(l,mid-1);
solve(mid+1,r);
int len=pos[mid].size();
for(int i=0;i<len;i++)
{
int now=pos[mid][i];
res[now]=1ll*h[mid]*(qr[now]-ql[now]+1);
if(ql[now]<mid)
{
res[now]=min(res[now],s.query_left(1,1,n,ql[now])+1ll*h[mid]*(qr[now]-mid+1));
}
if(qr[now]>mid)
{
res[now]=min(res[now],t.query_right(1,1,n,qr[now])+1ll*h[mid]*(mid-ql[now]+1));
}
}
ll sx=h[mid];
ll tx=h[mid];
if(l<mid)
{
tx+=t.query_right(1,1,n,mid-1);
}
if(r>mid)
{
sx+=s.query_left(1,1,n,mid+1);
}
s.change(1,1,n,mid,mid,sx);
t.change(1,1,n,mid,mid,tx);
if(l<mid)
{
s.change(1,1,n,l,mid-1,1ll*h[mid]*(r-mid+1));
s.merge(1,1,n,l,mid-1,-1ll*h[mid],sx+1ll*mid*h[mid]);
}
if(r>mid)
{
t.change(1,1,n,mid+1,r,1ll*h[mid]*(mid-l+1));
t.merge(1,1,n,mid+1,r,1ll*h[mid],tx-1ll*mid*h[mid]);
}
}
vector<ll> minimum_costs(vector<int> H, vector<int> L,vector<int> R)
{
n=H.size();
q=L.size();
for(int i=1;i<=n;i++)
{
h[i]=H[i-1];
f[i][0]=h[i];
g[i][0]=i;
}
for(int i=1;i<=q;i++)
{
ql[i]=L[i-1];
qr[i]=R[i-1];
ql[i]++;
qr[i]++;
}
for(int i=2;i<=n;i++)
{
ln[i]=ln[i>>1]+1;
}
for(int j=1;j<=20;j++)
{
for(int i=1;i<=n;i++)
{
if(i+(1<<j)-1>n)
{
break;
}
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
if(f[i][j]==f[i][j-1])
{
g[i][j]=g[i][j-1];
}
else
{
g[i][j]=g[i+(1<<(j-1))][j-1];
}
}
}
for(int i=1;i<=q;i++)
{
pos[cmp(ql[i],qr[i])].push_back(i);
}
solve(1,n);
return vector<ll>(res+1,res+q+1);
}
[IOI2018]会议——分治+线段树的更多相关文章
- UVALive 7148 LRIP【树分治+线段树】
题意就是要求一棵树上的最长不下降序列,同时不下降序列的最小值与最大值不超过D. 做法是树分治+线段树,假设树根是x,y是其当前需要处理的子树,对于子树y,需要处理出两个数组MN,MX,MN[i]表示以 ...
- 【loj6145】「2017 山东三轮集训 Day7」Easy 动态点分治+线段树
题目描述 给你一棵 $n$ 个点的树,边有边权.$m$ 次询问,每次给出 $l$ .$r$ .$x$ ,求 $\text{Min}_{i=l}^r\text{dis}(i,x)$ . $n,m\le ...
- 【BZOJ4372】烁烁的游戏 动态树分治+线段树
[BZOJ4372]烁烁的游戏 Description 背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠.题意:给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠.烁烁他每次会跳到一个节点u,把周围与他距 ...
- 【bzoj4372】烁烁的游戏 动态点分治+线段树
题目描述 给一颗n个节点的树,边权均为1,初始点权均为0,m次操作:Q x:询问x的点权.M x d w:将树上与节点x距离不超过d的节点的点权均加上w. 输入 第一行两个正整数:n,m接下来的n-1 ...
- 【bzoj3730】震波 动态点分治+线段树
题目描述 在一片土地上有N个城市,通过N-1条无向边互相连接,形成一棵树的结构,相邻两个城市的距离为1,其中第i个城市的价值为value[i].不幸的是,这片土地常常发生地震,并且随着时代的发展,城市 ...
- 洛谷T44252 线索_分治线段树_思维题
分治线段树,其实就是将标记永久化,到最后再统一下传所有标记. 至于先后顺序,可以给每个节点开一个时间戳. 一般地,分治线段树用于离线,只查询一次答案的题目. 本题中,标记要被下传 222 次. Cod ...
- 2019ICPC上海网络赛A 边分治+线段树
题目: 给定一棵树, 带边权. 现在有2种操作: 1.修改第i条边的权值. 2.询问u到其他一个任意点的最大距离是多少. 解法:边分治+线段树 首先我们将所有的点修改和边修改都存在对应的边里面. 然后 ...
- [BZOJ1018][SHOI2008]堵塞的交通traffic 时间分治线段树
题面 介绍一种比较慢的但是好想的做法. 网上漫天的线段树维护联通性,然后想起来费很大周折也很麻烦.我的做法也是要用线段树的,不过用法完全不同. 这个东西叫做时间分治线段树. 首先我们建一个\(1..m ...
- 牛客多校第三场 G Removing Stones(分治+线段树)
牛客多校第三场 G Removing Stones(分治+线段树) 题意: 给你n个数,问你有多少个长度不小于2的连续子序列,使得其中最大元素不大于所有元素和的一半 题解: 分治+线段树 线段树维护最 ...
随机推荐
- 查看Orcale数据里的表是否有变化
由于我们公司一个数据库两个应用在使用,导致一个应用修改了数据库,另一个应用用的缓存而不知道有更新还是原来的结果.原来的处理方式是采用session缓存的方式,用户登出了就清空缓存,这样只需要重新登录一 ...
- linux 修改内核参数 如何生效?
Linux 操作系统修改内核参数有3种方式: 修改 /etc/sysctl.conf 文件,加入配置选项,格式为 key = value ,修改保存后调用 sysctl -p 加载新配置使用 sysc ...
- (转)怎么实时查看mysql当前连接数
1.查看当前所有连接的详细资料: ./mysqladmin -uadmin -p123456 -h127.0.0.1 processlist 2.只查看当前连接数(Threads就是连接数.): ./ ...
- 开源HTTP解析器---http-parser和fast-http
由于项目中遇到需要发送http请求,然后再解析接收到的响应.大概在网上搜索了一下,有两个比较不错,分别是http-parser和fast-http. http-parser是由C编写的工具:fast- ...
- jquery中的选择器:has和:not的用法
这两个选择器可以帮助我们在选择父级和子孙之间关系的dom更从容~ <div><p><span>Hello</span></p></di ...
- Luogu P4587 [FJOI2016]神秘数
一道好冷门的好题啊,算是对于一个小结论和数据结构的一点考验吧 首先看完题目我们发现要从这个神秘数的性质入手,我们观察or手玩可得: 如果有\(x\)个\(1\),那么\([1,x]\)都是可以表示出来 ...
- 【SQL】四种排序开窗函数
一 .简单了解什么是开窗函数 什么是开窗函数,开窗函数有什么作用,特征是什么? 所谓开窗函数就是定义一个行为列,简单讲,就是在你查询的结果上,直接多出一列值(可以是聚合值或是排序号),特征就是带有ov ...
- 基于uFUN开发板的心率计(三)Qt上位机的实现
前言 上两周利用周末的时间,分别写了基于uFUN开发板的心率计(一)DMA方式获取传感器数据和基于uFUN开发板的心率计(二)动态阈值算法获取心率值,介绍了AD采集传感器数据和数据的滤波处理获取心率值 ...
- Linux查看日志常用命令
1.动态循环查看文件内容 tail -n 10 test.log 查询日志尾部最后10行的日志; tail -n +10 test.log 查询10行之后的所有日志; head -n ...
- linux下安装redis组件报错-gcc报错
报错如图: 1.解决办法 先安装gcc插件.删除redis解压后文件.重新解压