洛谷P4425 转盘 [HNOI/AHOI2018] 线段树+单调栈

正解:线段树+单调栈

解题报告:

传送门!

1551又是一道灵巧连题意都麻油看懂的题,,,,所以先解释一下题意好了,,,,

给定一个n元环

可以从0时刻开始从任一位置出发

每次可以选择向前走一步或者在原地不动

当到第i个点的时间ti>=Ti的时候点i被标记

问标记完整个环最少要多久时间

(Ti带修改QAQ

首先有几个会用到的性质大概港下

1)只要走一圈就足够

试证如下:

可以考虑反过来看这个问题,若现在已知标记的最后一个点的时间为Tim,那么题目就变成辣,现在时间为T,每次可以向后倒退一步或不动,每个点有一个消失时间Ti,必须要在消失时间之前到达这个点

那么显然只要走一圈就足够

(其实直接感性理解一下就好昂,,,显然再走一圈不会更好,,,有什么可证的QAQ

2)肯定选择向前走,在原地是不可能的

不需要证,随便说下趴(其实一样感性理解就很显然昂,,,?

依然反着考虑,现在变成要求Timmin对趴,然后还要在Ti之前到达对趴,那显然肯定一直跑下去昂为什么要停下来,,,所以我jio得我还是在感性理解并麻油理性解释,,,不管了不管了反正就感性理解一下就好辣QAQ

然后通过这两个性质我们就可以得到最优解一定是什么样儿的:

1)只走一圈

2)中途不会停留,就算停留也是在开始停一段时间

(就是说如果在Tim那儿倒叙做的时候发现最后到了起始节点的时候Tim还是有值的那么就要在最初停Tim的时间

综上,可以得到式子Tim=在第一个节点停留的时间Time+n-1

再思考,Time怎么算?想到Time必然满足Time+dis(i,j)>=tj,变形得Time>=tj-j+i,即Time=max(tj-j+i)

然后现在是找一个i使得max(tj-j+i)min

考虑到ti和i都只和i有关且都是定值,设ai表示ti-i

然后就变成了求min(max(aj)+i),其中j<=2*n(本来是j<=i+n嘛,但考虑到j∈[i+n+1,2n]的时候是不可能有贡献的,所以可以扩大范围麻油影响

然后只看这个max(aj)的话就是个后缀最大值,显然能构成一个单调栈

然后对于这个i,其实可以发现,aj是对应的一个区间嘛,那既然要min所以就i是这个区间的左端点,也就是说会是一个定值

所以如果考虑麻油修改的话就是从后往前维护一个单调栈

有修改就用线段树维护一下就好,实现比较简单,具体可以看楼房重建这道题,都差不多,依然是求mx,然后lth变成了存最左端点的区间的答案而已,实现方法以及思想什么的还是比较一致的w

 

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define rg register
#define gc getchar()
#define ls(x) (x<<1)
#define rs(x) ((x<<1)|1)
#define rp(i,x,y) for(rg int i=x;i<=y;++i)

const int N=1e5+10,inf=0x7fffffff;
int n,m,op,val[N<<1],as;
struct sgtr{int l,r,mx,as;}tr[N<<3];

il int read()
{
    char ch=gc;int x=0;bool y=1;
    while(ch!='-' && (ch>'9' || ch<'0'))ch=gc;
    if(ch=='-')ch=gc,y=0;
    while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc;
    return y?x:-x;
}
il int query(int x,int dat)
{
//    printf("1.as=%d\n",tr[1].as);
//    printf("query : x=%d dat=%d l=%d r=%d\n\n",x,dat,tr[x].l,tr[x].r);
    if(tr[x].l==tr[x].r)return tr[x].l+dat+1;
    if(tr[rs(x)].mx<=dat)return query(ls(x),dat);
    return min(tr[x].as,query(rs(x),dat));
}
il void pushup(int x)
{
//    printf("1.as=%d\n",tr[1].as);
//    printf("pushup : x=%d ls=%d rs=%d mx=%d\n",x,ls(x),rs(x),tr[rs(x)].mx);
    tr[x].mx=max(tr[ls(x)].mx,tr[rs(x)].mx);tr[x].as=query(ls(x),tr[rs(x)].mx);
//    printf("pushup x=%d mx=%d\n",x,tr[x].mx);
}
il void build(int x,int l,int r)
{
//    printf("1.as=%d\n",tr[1].as);
//    printf("build : x=%d l=%d r=%d\n",x,l,r);
    tr[x].l=l;tr[x].r=r;tr[x].as=inf;
    if(l==r)return void(tr[x].mx=val[l]-l);
    int mid=(l+r)>>1;build(ls(x),l,mid);build(rs(x),mid+1,r);pushup(x);
}
il void modify(int x,int dat)
{
//    printf("1.as=%d\n",tr[1].as);
//    printf("modify : x=%d dat=%d\n",x,dat);
    if(tr[x].l==tr[x].r)return void(tr[x].mx=val[dat]-dat);
    int mid=(tr[x].l+tr[x].r)>>1;if(mid>=dat)modify(ls(x),dat);else modify(rs(x),dat);
    pushup(x);
}

int main()
{
    // freopen("zp.in","r",stdin);freopen("zp.out","w",stdout);
    n=read();m=read();op=read();rp(i,1,n)val[i]=val[i+n]=read();
    build(1,1,n<<1);printf("%d\n",as=tr[1].as+n-1);
    rp(i,1,m)
    {
        int x=read()^(as*op),y=read()^(as*op);val[x]=val[x+n]=y;
        modify(1,x);modify(1,x+n);printf("%d\n",as=tr[1].as+n-1);
    }
    return 0;
}
这儿是代码QAQ
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值