Luogu P4425 转盘 题解 [ 黑 ] [ 线段树 ] [ 贪心 ] [ 递归 ]
转盘:蒟蒻的第一道黑,这题是贪心和线段树递归合并的综合题。
贪心
破环成链的 trick 自然不用多说。
首先观察题目,很容易发现一个性质:只走一圈的方案一定最优。这个很容易证,因为再绕一圈回来标记前面的和等前面的标记完之后继续走是等价的,并且再绕一圈甚至可能更劣。
于是,我们只用走一圈,并且在走路的途中走走停停,就可以走出最优解。
但这依然不怎么好求,通过观察发现:我们把所有停下的时间移到前面来,在起点就把要停的时间全部停掉,和走走停停是等价的,并且这样更好求。
接下来我们就来推式子了:
假设当前的时间是 \(t\),我们从 \(i\) 出发,要走到 \(j\) 处,并且 \(j\) 处的最早出现时间为 \(a_j\),那么可以列出方程:
\]
移项得:
\]
因此在此时 \(t\) 的最小值就是 \(\max_{j=i}^{i+n-1}(a_j-j+i)\)。
由于每次走路还要计算时间,所以总时间为 \(\max_{j=i}^{i+n-1}(a_j-j+i)+n-1\)。
因为要最小化,所以答案就是 \(\min_{i=1}^{n}(\max_{j=i}^{i+n-1}(a_j-j+i))+n-1\)。
线段树与递归
根据上面的分析,我们需要维护 \(\min_{i=1}^{n}(\max_{j=i}^{i+n-1}(a_j-j+i))+n-1\) 这个式子。
首先 \(\max_{j=i}^{i+n-1}(a_j-j+i)\) 是很好维护的,我们可以通过一个线段树来维护。
但是外面的那个 \(\min_{i=1}^{n}\) 怎么维护?实际上我们可以通过在线段树上递归来实现。
设 \(x\) 表示线段树上某个节点的最小值。因为我们只需要求出起点在 \(1\) 到 \(n\) 中的最小值(后面一部分是复制两倍后的,所以和前面的重复了,可以不要计算),所以 \(x\) 就是这个节点左半部分的最小值,而不是整个节点的最小值。但也不是说按整个节点算就不可以了,只是说这样做代码好写、思路好想一些。
接下来考虑如何递归合并信息:
注意:当前我们处在的节点是 \(l\)。
当递归到叶子节点时
由式子可知,直接返回 \(i+\max(lson_{max},r_{max})\) 即可。
当 \(rson_{max}< r_{max}\) 时
\(r_{max}\) 依然是最大值,所以要接下来递归 \(l\) 的左儿子 \(lson\),因为右儿子已经确定贡献了,贡献就是 \(\min(dfs(lson,r_{max}),mid+1+r_{max})\)。
当 \(rson_{max}\ge r_{max}\) 时
\(rson_{max}\) 把 \(r_{max}\) 挤下去了,所以要接下来递归 \(l\) 的右儿子 \(rson\),以找到右儿子的那个最大值在哪,才能确定贡献,左儿子的贡献就是 \(\min(x_l,dfs(rson,r_{max}))\)。
注意 \(x_l\) 与 \(dfs(p,r_{max})\) 函数的定义不同,其一是表示左半部分的最小值,另一个是表示全部区间的最小值。
其余部分就是按照线段树常规的配置来写了。
代码
#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
using namespace std;
typedef long long ll;
typedef pair<int,int> pi;
const int N=200005;
int n,m,q,a[N],lastans=0;
struct node{
int l,r;
int mx,mi;
}tr[4*N];
int dfs(int p,int mxr)
{
if(tr[p].l==tr[p].r)return (tr[p].l+max(mxr,tr[p].mx));
int mid=(tr[p].l+tr[p].r)>>1;
if(tr[rc].mx<mxr)return min(dfs(lc,mxr),mid+1+mxr);
return min(tr[p].mi,dfs(rc,mxr));
}
void pushup(int p)
{
tr[p].mx=max(tr[lc].mx,tr[rc].mx);
tr[p].mi=dfs(lc,tr[rc].mx);
}
void build(int p,int ln,int rn)
{
tr[p]={ln,rn,a[ln]-ln,ln+a[ln]-ln};
if(ln==rn)return;
int mid=(ln+rn)>>1;
build(lc,ln,mid);
build(rc,mid+1,rn);
pushup(p);
}
void update(int p,int x,int v)
{
if(tr[p].l==x&&tr[p].r==x)
{
tr[p].mx=v-x;
tr[p].mi=x+v-x;
return;
}
int mid=(tr[p].l+tr[p].r)>>1;
if(x<=mid)update(lc,x,v);
else update(rc,x,v);
pushup(p);
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m>>q;
for(int i=1;i<=n;i++)cin>>a[i],a[i+n]=a[i];
build(1,1,2*n);
cout<<tr[1].mi+n-1<<endl;
lastans=tr[1].mi+n-1;
while(m--)
{
int x,y;
cin>>x>>y;
if(q)x^=lastans,y^=lastans;
update(1,x,y);
update(1,x+n,y);
cout<<tr[1].mi+n-1<<endl;
lastans=tr[1].mi+n-1;
}
return 0;
}
Luogu P4425 转盘 题解 [ 黑 ] [ 线段树 ] [ 贪心 ] [ 递归 ]的更多相关文章
- [火星补锅] 水题大战Vol.2 T1 && luogu P1904 天际线 题解 (线段树)
前言: 当时考场上并没有想出来...后来也是看了题解才明白 解析: 大家(除了我)都知道,奇点和偶点会成对出现,而出现的前提就是建筑的高度突然发生变化.(这个性质挺重要的,我之前没看出来) 所以就可以 ...
- 洛谷P4425 转盘 [HNOI/AHOI2018] 线段树+单调栈
正解:线段树+单调栈 解题报告: 传送门! 1551又是一道灵巧连题意都麻油看懂的题,,,,所以先解释一下题意好了,,,, 给定一个n元环 可以从0时刻开始从任一位置出发 每次可以选择向前走一步或者在 ...
- Bzoj5251 线段树+贪心
Bzoj5251 线段树+贪心 记录本蒟蒻省选后的第一篇题解!国际惯例的题面:首先这个东西显然是一棵树.如果我们把数值排序,并建立这棵树的dfs序,显然dfs序上的一个区间对应数值的一个区间,且根为数 ...
- BZOJ_1826_[JSOI2010]缓存交换 _线段树+贪心
BZOJ_1826_[JSOI2010]缓存交换 _线段树+贪心 Description 在计算机中,CPU只能和高速缓存Cache直接交换数据.当所需的内存单元不在Cache中时,则需要从主存里把数 ...
- 2018.10.20 NOIP模拟 蛋糕(线段树+贪心/lis)
传送门 听说是最长反链衍生出的对偶定理就能秒了. 本蒟蒻直接用线段树模拟维护的. 对于第一维排序. 维护第二维的偏序关系可以借助线段树/树状数组维护逆序对的思想建立权值线段树贪心求解. 代码
- luogu P2574 XOR的艺术 (线段树)
luogu P2574 XOR的艺术 (线段树) 算是比较简单的线段树. 当区间修改时.\(1 xor 1 = 0,0 xor 1 = 1\)所以就是区间元素个数减去以前的\(1\)的个数就是现在\( ...
- 【原创】洛谷 LUOGU P3373 【模板】线段树2
P3373 [模板]线段树 2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格式: 第 ...
- 【原创】洛谷 LUOGU P3372 【模板】线段树1
P3372 [模板]线段树 1 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.求出某区间每一个数的和 输入输出格式 输入格式: 第一行包含两个整数N.M,分别 ...
- 【Luogu】P1607庙会班车Fair Shuttle(线段树+贪心)
我不会做贪心题啊……贪心题啊……题啊……啊…… 我真TM菜爆了啊…… 这题就像凌乱的yyy一样,把终点排序,终点相同的按起点排序.然后维护一个查询最大值的线段树.对于一个区间[l,r],如果这个区间已 ...
- codeforces 675E Trains and Statistic 线段树+贪心统计
分析:这个题刚看起来无从下手 但是我们可以先简化问题,首先可以固定起点i,求出i+1到n的最小距离 它可以到达的范围是[i+1,a[i]],贪心的想,我们希望换一次车可以到达的距离尽量远 即:找一个k ...
随机推荐
- Linux之密码生成工具pwgen
linux中生成随机字符串,可以使用pwgen 安装) ubuntu: apt-get install pwgen Centos: yum install pwgen 语法及参数) pwgen [ O ...
- yum之镜像加速
有没有遇到使用yum安装软件慢如龟,默认的系统使用的是centos的镜像源,我们可以修改为国内镜像源加速软件安装 163)http://mirrors.163.com/.help/centos.htm ...
- 【ElementPlus】el-form使用技巧:动态切换校验规则的最佳实践
喵~ 今天分享一篇在 ElementPlus 中使用 el-form 动态切换校验规则 的实用方法. 一.问题概述 作为前端开发人员,在开发项目中,特别是后台管理系统,表单的使用是必不可少的.当业务需 ...
- Vue 模版解析
1.大括号表达式 (1)在MVVM()中接收并保存配置对象 (2)调用Compile编译函数,将el和vm传入 function MVVM (option) { this.$option = opti ...
- K8S钩子、探针以及控制器完整版
一. 生命周期钩子 Kubernetes 中的 生命周期钩子(Lifecycle Hooks) 是在容器生命周期的特定阶段执行操作的机制.通过钩子,可以在容器启动后(PostStart)或停止前(Pr ...
- .NET Core 堆结构(Heap)底层原理浅谈
.Net托管堆布局 加载堆 主要是供CLR内部使用,作为承载程序的元数据. HighFrequencyHeap 存放CLR高频使用的内部数据,比如MethodTable,MethodDesc. 通过i ...
- MongoDB|TOMCAT定时切割日志文件的脚本
MongoDB用过一段时间后,日志较大,需要定时进行日志切割. 一.切割bash: splitlogmongo.sh #!/bin/bash log_dir="/home/mongodb/l ...
- JAVA-通过大疆TSDK的API直接获取红外图片温度信息
一.前言 看过很多关于大疆红外图片用TSDK取温的方式,但是网上能搜到的大部分教程都是通过官方下载文件smple编译出来的程序来取温,如果这样做,虽然确实也能够实现目的,但不得不说,不但会降低运行速度 ...
- 【PHP】连接数据库验证登陆
界面 <!doctype html> <html lang="en"> <head> <!-- Required meta tags -- ...
- HTMLreport报告(五) -- 测试报告中添加截图
一.需求痛点 HTMLreport报告没有截图 二.实现办法 1.思路:使用viewer.js图片查看器,用cdn:后端部分用 unittest.test_result中的内容 2.实现步骤 1)vi ...