显然最优走法是先一直停在初始位置然后一次性走完一圈。将序列倍长后,相当于找一个长度为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. python爬取快手ios端首页热门视频

    最近快手这种小视频app,特别的火,中午吃过午饭,闲来无聊,想搞下快手的短视频,看能不能搞到. 于是乎, 打开了fiddler,开始准备抓包, 设置代理,重启,下一步,查看本机ip 手机打开网络设置 ...

  2. 【日常训练】Help Chef Gerasim(Codeforces 99B)

    题意与分析 需要注意非法情况.换言之,合法情况其实很苛刻. 代码 /* * ACM Code => cf99b.java * Written by Sam X * Date: 三月, 19, 2 ...

  3. Java编辑PPT的柱状图,与内嵌的Excel联动

    /** * 条形图:柱形图 的数据写入方法 * @param slide 图表 * @param index 柱状图的下标 * @param data 要填充的数据 * @param titles 内 ...

  4. 初学node.js-npm使用(2)

    1.安装Node封装模块 安装Node封装模块很重要,因为开发项目中会用到各种各样的功能,这时就需要去下载开源的模块 使用npm install <module_name> module_ ...

  5. 【异常检测】Isolation forest 的spark 分布式实现

    1.算法简介 算法的原始论文 http://cs.nju.edu.cn/zhouzh/zhouzh.files/publication/icdm08b.pdf .python的sklearn中已经实现 ...

  6. printf命令详解

    基础命令学习目录首页 本文是Linux Shell系列教程的第(八)篇,更多shell教程请看:Linux Shell系列教程 在上一篇:Linux Shell系列教程之(七)Shell输出这篇文章中 ...

  7. Python参数传递,既不是传值也不是传引用

    面试的时候,有没有被问到Python传参是传引用还是传值这种问题?有没有听到过Python传参既不是传值也不是传引用这种说法?一个小小的参数默认值也可能让代码出现难以查找的bug? 如果你也遇到过上面 ...

  8. 前端_JavaScript

    目录 JavaScript的基础 引入方式 JS的变量.常量和标识符 JS的数据类型 运算符 流程控制 JavaScript的对象 String对象 Array对象 Date对象 Math对象 Fun ...

  9. Beta版发布 - 感谢有你们

    在本次Beta版开发的过程中,很感谢组长王航对我的信任,让我统筹大家的工作任务和进度,使我对项目管理有了深刻的理解. 我也要感谢邹双黛,在beta版开发中因为邹双黛的帮助,我对于文字类工作已经越来越得 ...

  10. Daily Scrum3 11.5

    昨天的任务已经完成,但是大家分析后发现进度稍有些慢.今天各自都在调整进度,不再拖延别人的工作. 今日任务: 杨伊:做问卷调查,准备用户体验篇内容. 徐钧鸿:把Xueba中Utility 向闸瓦移植 张 ...