显然最优走法是先一直停在初始位置然后一次性走完一圈。将序列倍长后,相当于找一个长度为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转盘(分块/线段树)的更多相关文章

  1. 【BZOJ5286】[HNOI2018]转盘(线段树)

    [BZOJ5286][HNOI2018]转盘(线段树) 题面 BZOJ 洛谷 题解 很妙的一道题目啊.(全世界除了我这题都有40分,就我是一个状压选手 首先来发现一些性质,我们走一圈一定不会更差. 为 ...

  2. [HNOI/AHOI2018]转盘(线段树优化单调)

    gugu  bz lei了lei了,事独流体毒瘤题 一句话题意:任选一个点开始,每个时刻向前走一步或者站着不动 问实现每一个点都在$T_i$之后被访问到的最短时间 Step 1 该题可证: 最优方案必 ...

  3. 洛谷P4425 [HNOI/AHOI2018]转盘(线段树)

    题意 题目链接 Sol 首先猜一个结论:对于每次询问,枚举一个起点然后不断等到某个点出现时才走到下一个点一定是最优的. 证明不会,考场上拍了3w组没错应该就是对的吧... 首先把数组倍长一下方便枚举起 ...

  4. [BZOJ5286][洛谷P4425][HNOI2018]转盘(线段树)

    5286: [Hnoi2018]转盘 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 15  Solved: 11[Submit][Status][Di ...

  5. CDOJ 1157 数列(seq) 分块+线段树

    数列(seq) Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://acm.uestc.edu.cn/#/problem/show/1157 Desc ...

  6. CDOJ 1292 卿学姐种花 暴力 分块 线段树

    卿学姐种花 题目连接: http://acm.uestc.edu.cn/#/problem/show/1292 Description 众所周知,在喵哈哈村,有一个温柔善良的卿学姐. 卿学姐喜欢和她一 ...

  7. BZOJ - 2957 (分块/线段树)

    题目链接 本质是维护斜率递增序列. 用分块的方法就是把序列分成sqrt(n)块,每个块分别用一个vector维护递增序列.查询的时候遍历所有的块,同时维护当前最大斜率,二分找到每个块中比当前最大斜率大 ...

  8. [HNOI/AHOI2018]转盘

    一个结论:一定存在一个最优解只走一圈.否则考虑从最后一个结束位置开始一定可以达到相同效果 画个图,类似是一种斜线感觉 考虑一个高度贡献的最高点 对于i开始的连续n个,答案是:max(Tj-j)+i+n ...

  9. [HNOI2018]转盘[结论+线段树]

    题意 题目链接 分析 首先要发现一个结论:最优决策一定存在一种 先在出发点停留之后走一圈 的情况,可以考虑如下证明: 如果要停留的话一定在出发点停留,这样后面的位置更容易取到. 走超过两圈的情况都可以 ...

随机推荐

  1. h5小球走迷宫小游戏源码

    无意中找到的一个挺有意思的小游戏,关键是用h5写的,下面就分享给大家源码 还是先来看小游戏的截图 可以用键盘的三个键去控制它,然后通关 下面是源代码 <!doctype html> < ...

  2. C# 通过copydata实现进程间通信

    最近公司需要实现一个基于copydata进程间通信的功能.原来一直没有接触过Windows的进程通信,这次正好可以学习一下. 程序是基于Winform的,下面直接上代码. 公共类: public cl ...

  3. ETCD分布式存储部署

    一.ETCD 概述 ETCD 是一个分布式一致性k-v存储系统,可用于服务注册发现与共享配置.具有一下优点: 简单: 相比于晦涩难懂的paxos算法,etcd基于相对简单且易实现的raft算法实现一致 ...

  4. TPO-19 C2 Cafeteria's Food Policy

    TPO-19 C2 Cafeteria's Food Policy 第 1 段 1.Listen to a conversation between a student and the directo ...

  5. Andorid Studio 模块化开发相关配置

    Andorid Studio 模块化开发相关配置 下面以宿主APP模块和Uer_Module模块为例: 第一步:在项目根目录gradle.properties配置文件中添加如下代码 isNeedUse ...

  6. 关于kafka的一些问题理解

  7. move.js运动插件

    move.js 运动插件是一款针对元素动画效果的插件.可以运用此插件制作出各类元素效果. 插件GitHub地址:https://github.com/visionmedia/move.js 下面整理学 ...

  8. Streamr助你掌控自己的数据(1)——教你5分钟上传数据至Streamr

    博客说明 所有刊发内容均可转载但是需要注明出处. 教你5分钟上传数据至Streamr 本系列文档主要介绍怎么通过Streamr管理自己的DATA,整个系列包括三篇教程文档,分别是:教你5分钟上传数据至 ...

  9. 工作小应用:EXCEL查找两列重复数据

    工作案例:excel存在A列.B列,需要找出B列没有A列的数据,具体做法如下(以office2007做案例): 1.点击 公式-定义名称 ,选中A列,填写名称“AAA”,选中B列,填写名称“BBB”: ...

  10. Hadoop Streaming框架使用(二)

    上一篇文章介绍了Streaming的各种参数,本文具体介绍使用方法. 提交hadoop任务示例: $HADOOP_HOME/bin/hadoop streaming \ -input /user/te ...