BZOJ5286 HNOI/AHOI2018转盘(分块/线段树)
显然最优走法是先一直停在初始位置然后一次性走完一圈。将序列倍长后,相当于找一个长度为n的区间[l,l+n),使其中ti+l+n-1-i的最大值最小。容易发现ti-i>ti+n-(i+n),所以也就相当于是后缀最大值最小。设ti-i=ai,即要求min{l+max{al..2n}}+n-1 (l=1..n)。如果没有修改的话只要扫一遍就行了。
线段树看起来很难维护,考虑分块。每一块求出仅考虑该块的ai时上述值的前缀min和ai的后缀max。对于查询,从后往前考虑所选区间左端点在哪一块。如果该块某个位置出现了整个序列的后缀最大值,序列后面的部分就不会对该块之前位置的答案产生影响,可以直接使用之前求出的答案。于是根据后缀最大值将该块划分成两部分,后一部分由于max{ai}被固定为后缀最大值,当然选择尽量左的点时最优。修改时暴力重构块即可。块大小取sqrt(nlogn)时最优,为O(msqrt(nlogn))。没有卡常也在慢如狗的bzoj上只跑了11s。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 200010
#define inf 2000000010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<''||c>'')) c=getchar();return c;}
int gcd(int n,int m){return m==?n:gcd(m,n%m);}
int read()
{
int x=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
}
int n,m,op,a[N],L[N],R[N],pos[N],mx[N],mn[N],block,num,ans=inf;
void build(int x)
{
mx[R[x]]=a[R[x]];mn[R[x]]=R[x]+a[R[x]];
for (int i=R[x]-;i>=L[x];i--)
mn[i]=i+(mx[i]=max(mx[i+],a[i]));
for (int i=L[x]+;i<=R[x];i++)
mn[i]=min(mn[i-],mn[i]);
}
int query()
{
int u=-inf,ans=inf;
for (int i=*num;i>num;i--) u=max(u,mx[L[i]]);
for (int i=num;i>=;i--)
{
int l=L[i],r=R[i],x=R[i]+;
while (l<=r)
{
int mid=l+r>>;
if (mx[mid]<=u) x=mid,r=mid-;
else l=mid+;
}
ans=min(ans,x+u);if (x>L[i]) ans=min(ans,mn[x-]);
u=max(u,mx[L[i]]);
}
return ans;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj5286.in","r",stdin);
freopen("bzoj5286.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
n=read(),m=read(),op=read();block=sqrt(n*log(n+));num=(n-)/block+;
for (int i=;i<=n;i++) a[i]=a[i+n]=read();
for (int i=;i<=n*;i++) a[i]-=i;
for (int i=;i<=num;i++)
{
L[i]=R[i-]+,R[i]=min(n,L[i]+block-);
for (int j=L[i];j<=R[i];j++)
pos[j]=i;
build(i);
}
for (int i=num+;i<=*num;i++)
{
L[i]=R[i-]+,R[i]=min(*n,L[i]+block-);
for (int j=L[i];j<=R[i];j++)
pos[j]=i;
build(i);
}
ans=query()+n-;cout<<ans<<endl;
while (m--)
{
int x=read()^ans*op,y=read()^ans*op;
a[x]=y-x,a[x+n]=y-x-n;build(pos[x]),build(pos[x+n]);
printf("%d\n",ans=query()+n-);
}
return ;
}
事实上这个做法可以扩展到线段树上(其实完全没有任何相似的地方吧?)。考虑每个节点维护该区间的最大值和仅考虑该区间ai时左半部分的最优答案。只要解决如何合并两个区间就可以了。类似的,右边的区间可以直接返回答案,然后考虑左节点的右半部分和右节点最大值的大小,如果左节点较大,直接返回左节点的左半部分答案并递归右节点,否则递归左节点。于是复杂度即为O(nlog2n)。鬼知道我在干什么莫名其妙写了一晚上。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 200010
#define inf 2000000010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<''||c>'')) c=getchar();return c;}
int gcd(int n,int m){return m==?n:gcd(m,n%m);}
int read()
{
int x=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
}
int n,m,op,a[N],ans=inf;
struct data{int l,r,max,ans;
}tree[N<<];
int query(int k,int mx)
{
if (tree[k].l==tree[k].r) return tree[k].l+max(mx,tree[k].max);
if (tree[k<<|].max>=mx) return min(tree[k].ans,query(k<<|,mx));
else return min((tree[k].l+tree[k].r>>)++mx,query(k<<,mx));
}
void build(int k,int l,int r)
{
tree[k].l=l,tree[k].r=r;
if (l==r) {tree[k].max=a[l];return;}
int mid=l+r>>;
build(k<<,l,mid);
build(k<<|,mid+,r);
tree[k].max=max(tree[k<<].max,tree[k<<|].max);
tree[k].ans=query(k<<,tree[k<<|].max);
}
void modify(int k,int p,int x)
{
if (tree[k].l==tree[k].r) {tree[k].max=x;return;}
int mid=tree[k].l+tree[k].r>>;
if (p<=mid) modify(k<<,p,x);
else modify(k<<|,p,x);
tree[k].max=max(tree[k<<].max,tree[k<<|].max);
tree[k].ans=query(k<<,tree[k<<|].max);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj5286.in","r",stdin);
freopen("bzoj5286.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
n=read(),m=read(),op=read();
for (int i=;i<=n;i++) a[i]=a[i+n]=read();
for (int i=;i<=n*;i++) a[i]-=i;
build(,,*n);
ans=tree[].ans+n-;cout<<ans<<endl;
while (m--)
{
int x=read()^ans*op,y=read()^ans*op;
modify(,x,y-x),modify(,x+n,y-x-n);
printf("%d\n",ans=tree[].ans+n-);
}
return ;
}
BZOJ5286 HNOI/AHOI2018转盘(分块/线段树)的更多相关文章
- 【BZOJ5286】[HNOI2018]转盘(线段树)
[BZOJ5286][HNOI2018]转盘(线段树) 题面 BZOJ 洛谷 题解 很妙的一道题目啊.(全世界除了我这题都有40分,就我是一个状压选手 首先来发现一些性质,我们走一圈一定不会更差. 为 ...
- [HNOI/AHOI2018]转盘(线段树优化单调)
gugu bz lei了lei了,事独流体毒瘤题 一句话题意:任选一个点开始,每个时刻向前走一步或者站着不动 问实现每一个点都在$T_i$之后被访问到的最短时间 Step 1 该题可证: 最优方案必 ...
- 洛谷P4425 [HNOI/AHOI2018]转盘(线段树)
题意 题目链接 Sol 首先猜一个结论:对于每次询问,枚举一个起点然后不断等到某个点出现时才走到下一个点一定是最优的. 证明不会,考场上拍了3w组没错应该就是对的吧... 首先把数组倍长一下方便枚举起 ...
- [BZOJ5286][洛谷P4425][HNOI2018]转盘(线段树)
5286: [Hnoi2018]转盘 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 15 Solved: 11[Submit][Status][Di ...
- CDOJ 1157 数列(seq) 分块+线段树
数列(seq) Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://acm.uestc.edu.cn/#/problem/show/1157 Desc ...
- CDOJ 1292 卿学姐种花 暴力 分块 线段树
卿学姐种花 题目连接: http://acm.uestc.edu.cn/#/problem/show/1292 Description 众所周知,在喵哈哈村,有一个温柔善良的卿学姐. 卿学姐喜欢和她一 ...
- BZOJ - 2957 (分块/线段树)
题目链接 本质是维护斜率递增序列. 用分块的方法就是把序列分成sqrt(n)块,每个块分别用一个vector维护递增序列.查询的时候遍历所有的块,同时维护当前最大斜率,二分找到每个块中比当前最大斜率大 ...
- [HNOI/AHOI2018]转盘
一个结论:一定存在一个最优解只走一圈.否则考虑从最后一个结束位置开始一定可以达到相同效果 画个图,类似是一种斜线感觉 考虑一个高度贡献的最高点 对于i开始的连续n个,答案是:max(Tj-j)+i+n ...
- [HNOI2018]转盘[结论+线段树]
题意 题目链接 分析 首先要发现一个结论:最优决策一定存在一种 先在出发点停留之后走一圈 的情况,可以考虑如下证明: 如果要停留的话一定在出发点停留,这样后面的位置更容易取到. 走超过两圈的情况都可以 ...
随机推荐
- 从hs_strcpy谈安全——缓冲区溢出
对于大多数的博友来说,hs_strcpy一定会很陌生,因为这个hs_strcpy这个关键字和我的工作有挂钩.本来目前就职于恒生电子,hs_strcpy是中间件中公司定义的字符串拷贝方法,在工作业余之余 ...
- CSS动画transform、transition和animation的区别
CSS3属性中关于制作动画的三个属性:Transform,Transition,Animation. 1.transform:描述了元素的静态样式,本身不会呈现动画效果,可以对元素进行 旋转rotat ...
- lua中table的常用方法
转载:https://blog.csdn.net/Fenglele_Fans/article/details/83627021 1:table.sort() language = {"lua ...
- 文件的上传和下载--SpringMVC
文件的上传和下载是项目开发中最常用的功能,例如图片的上传和下载.邮件附件的上传和下载等. 接下来,将对Spring MVC环境中文件的上传和下载进行详细的讲解. 一.文件上传 多数文件上传都是通过表单 ...
- 傻瓜式搭建私有云就用这两组合:宝塔+kodexplorer
介绍 宝塔面板:是一款linux/windows平台均可使用的服务器管理软件,自带环境包,主要基于centos操作系统,可一键包装nginx.apache.php.mysql.pureftpd.php ...
- 图解 Go 并发
你很可能从某种途径听说过 Go 语言.它越来越受欢迎,并且有充分的理由可以证明. Go 快速.简单,有强大的社区支持.学习这门语言最令人兴奋的一点是它的并发模型. Go 的并发原语使创建多线程并发程序 ...
- telnet命令详解
基础命令学习目录 原文链接:https://www.cnblogs.com/PatrickLiu/p/8556762.html telnet命令用于登录远程主机,对远程主机进行管理.telnet因为采 ...
- ./configure的含义
在实践安装nginx的时候,不知道./configure是什么意思,这里特地记录一下. 在linux中./代表当前目录,属于相对路径../代表上一级目录,属于相对路径/代表根目录,/开头的文件都是绝对 ...
- Scrum立会报告+燃尽图(十月十一日总第二次):需求分析
此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2191 Scrum立会master:张俊余 一.小组介绍 组长:付佳 组员 ...
- js弹出框 -搜索
警告框alert() alert是警告框,只有一个按钮“确定”无返回值,警告框经常用于确保用户可以得到某些信息.当警告框出现后,用户需要点击确定按钮才能继续进行操作.语法:alert("文本 ...